forked from extern/nushell
Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
777f746127 |
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,2 +0,0 @@
|
|||||||
# Example of a `.gitattributes` file which reclassifies `.nu` files as Nushell:
|
|
||||||
*.nu linguist-language=Nushell
|
|
@@ -1,5 +0,0 @@
|
|||||||
#!/usr/bin/env nu
|
|
||||||
|
|
||||||
use ../toolkit.nu fmt
|
|
||||||
|
|
||||||
fmt --check --verbose
|
|
@@ -1,6 +0,0 @@
|
|||||||
#!/usr/bin/env nu
|
|
||||||
|
|
||||||
use ../toolkit.nu [fmt, clippy]
|
|
||||||
|
|
||||||
fmt --check --verbose
|
|
||||||
clippy --verbose
|
|
1
.github/AUTO_ISSUE_TEMPLATE/README.md
vendored
1
.github/AUTO_ISSUE_TEMPLATE/README.md
vendored
@@ -1 +0,0 @@
|
|||||||
This directory is intended for templates to automatically create issues with the [create-an-issue](https://github.com/JasonEtco/create-an-issue) action.
|
|
@@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
name: Nightly build of release binaries failed
|
|
||||||
about: Used to submit issues related to binaries release workflow
|
|
||||||
title: 'Attention: Nightly build of release binaries failed'
|
|
||||||
labels: ['build-package', 'priority']
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Nightly build of release binaries failed**
|
|
||||||
|
|
||||||
Hi there:
|
|
||||||
|
|
||||||
If you see me here that means there is a release failure for the nightly build
|
|
||||||
|
|
||||||
Please **click the status badge** to see more details: [](https://github.com/nushell/nushell/actions/workflows/nightly-build.yml)
|
|
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,6 +1,5 @@
|
|||||||
name: Bug Report
|
name: Bug Report
|
||||||
description: Create a report to help us improve
|
description: Create a report to help us improve
|
||||||
labels: ["needs-triage"]
|
|
||||||
body:
|
body:
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: description
|
id: description
|
||||||
|
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,6 +1,6 @@
|
|||||||
name: Feature Request
|
name: Feature Request
|
||||||
description: "When you want a new feature for something that doesn't already exist"
|
description: "When you want a new feature for something that doesn't already exist"
|
||||||
labels: ["needs-triage", "enhancement"]
|
labels: "enhancement"
|
||||||
body:
|
body:
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: problem
|
id: problem
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
name: standard library bug or feature report
|
|
||||||
about: Used to submit issues related to the nu standard library
|
|
||||||
title: ''
|
|
||||||
labels: ['needs-triage', 'std-library']
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug or feature**
|
|
||||||
A clear and concise description of what the bug is.
|
|
20
.github/dependabot.yml
vendored
20
.github/dependabot.yml
vendored
@@ -1,20 +0,0 @@
|
|||||||
# To get started with Dependabot version updates, you'll need to specify which
|
|
||||||
# package ecosystems to update and where the package manifests are located.
|
|
||||||
# Please see the documentation for all configuration options:
|
|
||||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
||||||
|
|
||||||
# docs
|
|
||||||
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "cargo"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
ignore:
|
|
||||||
- dependency-name: "*"
|
|
||||||
update-types: ["version-update:semver-patch"]
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
34
.github/pull_request_template.md
vendored
34
.github/pull_request_template.md
vendored
@@ -1,40 +1,24 @@
|
|||||||
<!--
|
|
||||||
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
|
# 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.
|
_(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.)_
|
||||||
|
|
||||||
# User-Facing Changes
|
# User-Facing Changes
|
||||||
<!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. -->
|
|
||||||
|
_(List of all changes that impact the user experience here. This helps us keep track of breaking changes.)_
|
||||||
|
|
||||||
# Tests + Formatting
|
# Tests + Formatting
|
||||||
<!--
|
|
||||||
Don't forget to add tests that cover your changes.
|
Don't forget to add tests that cover your changes.
|
||||||
|
|
||||||
Make sure you've run and fixed any issues with these commands:
|
Make sure you've run and fixed any issues with these commands:
|
||||||
|
|
||||||
- `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
|
- `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
|
||||||
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err` to check that you're using the standard code style
|
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
|
||||||
- `cargo test --workspace` to check that all tests pass
|
- `cargo test --workspace` to check that all tests pass
|
||||||
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the standard library
|
|
||||||
|
|
||||||
> **Note**
|
|
||||||
> from `nushell` you can also use the `toolkit` as follows
|
|
||||||
> ```bash
|
|
||||||
> use toolkit.nu # or use an `env_change` hook to activate it automatically
|
|
||||||
> toolkit check pr
|
|
||||||
> ```
|
|
||||||
-->
|
|
||||||
|
|
||||||
# After Submitting
|
# 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. -->
|
|
||||||
|
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.
|
||||||
|
154
.github/workflows/ci.yml
vendored
154
.github/workflows/ci.yml
vendored
@@ -11,28 +11,9 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
# Pinning to Ubuntu 20.04 because building on newer Ubuntu versions causes linux-gnu
|
platform: [windows-latest, macos-latest, ubuntu-latest]
|
||||||
# builds to link against a too-new-for-many-Linux-installs glibc version. Consider
|
|
||||||
# revisiting this when 20.04 is closer to EOL (April 2025)
|
|
||||||
platform: [windows-latest, macos-latest, ubuntu-20.04]
|
|
||||||
style: [default, dataframe, extra]
|
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
include:
|
|
||||||
- style: default
|
|
||||||
flags: ""
|
|
||||||
- style: dataframe
|
|
||||||
flags: "--features=dataframe "
|
|
||||||
- style: extra
|
|
||||||
flags: "--features=extra "
|
|
||||||
exclude:
|
|
||||||
# only test dataframes on Ubuntu (the fastest platform)
|
|
||||||
- platform: windows-latest
|
|
||||||
style: dataframe
|
|
||||||
- platform: macos-latest
|
|
||||||
style: dataframe
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
env:
|
env:
|
||||||
@@ -42,15 +23,19 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
||||||
with:
|
|
||||||
rustflags: ""
|
|
||||||
|
|
||||||
- name: cargo fmt
|
- name: Rustfmt
|
||||||
run: cargo fmt --all -- --check
|
uses: actions-rs/cargo@v1.0.1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --all -- --check
|
||||||
|
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
run: cargo clippy --workspace ${{ matrix.flags }}--exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err
|
uses: actions-rs/cargo@v1.0.1
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: --workspace --exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
||||||
|
|
||||||
nu-tests:
|
nu-tests:
|
||||||
env:
|
env:
|
||||||
@@ -59,8 +44,8 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
platform: [windows-latest, macos-latest, ubuntu-20.04]
|
platform: [windows-latest, macos-latest, ubuntu-latest]
|
||||||
style: [default, dataframe, extra]
|
style: [default, dataframe]
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
include:
|
include:
|
||||||
@@ -68,21 +53,12 @@ jobs:
|
|||||||
flags: ""
|
flags: ""
|
||||||
- style: dataframe
|
- style: dataframe
|
||||||
flags: "--features=dataframe"
|
flags: "--features=dataframe"
|
||||||
- style: extra
|
|
||||||
flags: "--features=extra"
|
|
||||||
|
|
||||||
exclude:
|
exclude:
|
||||||
# only test dataframes and extra on Ubuntu (the fastest platform)
|
# only test dataframes on Ubuntu (the fastest platform)
|
||||||
- platform: windows-latest
|
- platform: windows-latest
|
||||||
style: dataframe
|
style: dataframe
|
||||||
- platform: macos-latest
|
- platform: macos-latest
|
||||||
style: dataframe
|
style: dataframe
|
||||||
- platform: windows-latest
|
|
||||||
style: extra
|
|
||||||
- platform: macos-latest
|
|
||||||
style: extra
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
@@ -90,21 +66,22 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
||||||
with:
|
|
||||||
rustflags: ""
|
|
||||||
|
|
||||||
- name: Tests
|
- name: Tests
|
||||||
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
|
uses: actions-rs/cargo@v1.0.1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
|
||||||
|
|
||||||
std-lib-and-python-virtualenv:
|
python-virtualenv:
|
||||||
env:
|
env:
|
||||||
NU_LOG_LEVEL: DEBUG
|
NUSHELL_CARGO_TARGET: ci
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
platform: [ubuntu-20.04, macos-latest, windows-latest]
|
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
py:
|
py:
|
||||||
@@ -116,24 +93,13 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
||||||
with:
|
|
||||||
rustflags: ""
|
|
||||||
|
|
||||||
- name: Install Nushell
|
- name: Install Nushell
|
||||||
# prior to [*standard library: bring the tests into the main CI*](#8525)
|
uses: actions-rs/cargo@v1.0.1
|
||||||
# there was a `--profile ci` here in the `cargo install`, as well as
|
with:
|
||||||
# `NUSHELL_CARGO_TARGET: ci` in the prelude above.
|
command: install
|
||||||
#
|
args: --locked --path=. --profile ci --no-default-features
|
||||||
# this caused a "stackoverflow" error in the CI on windows,
|
|
||||||
# see [this failing job](https://github.com/nushell/nushell/actions/runs/4512034615/jobs/7944945590)
|
|
||||||
#
|
|
||||||
# the CI profile has been removed in 00b820de9021227d1910a9ea388297ee7aee308e
|
|
||||||
# as part of #8525.
|
|
||||||
run: cargo install --path . --locked --no-default-features
|
|
||||||
|
|
||||||
- name: Standard library tests
|
|
||||||
run: nu -c 'use std testing; testing run-tests --path crates/nu-std'
|
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
@@ -142,20 +108,12 @@ jobs:
|
|||||||
|
|
||||||
- run: python -m pip install tox
|
- run: python -m pip install tox
|
||||||
|
|
||||||
# Get only the latest tagged version for stability reasons
|
|
||||||
- name: Install virtualenv
|
- name: Install virtualenv
|
||||||
run: git clone https://github.com/pypa/virtualenv.git
|
run: git clone https://github.com/pypa/virtualenv.git
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Test Nushell in virtualenv
|
- name: Test Nushell in virtualenv
|
||||||
run: |
|
run: cd virtualenv && tox -e ${{ matrix.py }} -- -k nushell
|
||||||
cd virtualenv
|
|
||||||
# if we encounter problems with bleeding edge tests pin to the latest tag
|
|
||||||
# git checkout $(git describe --tags | cut -d - -f 1)
|
|
||||||
# We need to disable failing on coverage levels.
|
|
||||||
nu -c "open pyproject.toml | upsert tool.coverage.report.fail_under 1 | save patchproject.toml"
|
|
||||||
mv patchproject.toml pyproject.toml
|
|
||||||
tox -e ${{ matrix.py }} -- -k nushell
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
# Build+test plugins on their own, without the rest of Nu. This helps with CI parallelization and
|
# Build+test plugins on their own, without the rest of Nu. This helps with CI parallelization and
|
||||||
@@ -167,7 +125,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
platform: [windows-latest, macos-latest, ubuntu-20.04]
|
platform: [windows-latest, macos-latest, ubuntu-latest]
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
|
|
||||||
@@ -177,54 +135,16 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
||||||
with:
|
|
||||||
rustflags: ""
|
|
||||||
|
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
run: cargo clippy --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err
|
uses: actions-rs/cargo@v1.0.1
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
||||||
|
|
||||||
- name: Tests
|
- name: Tests
|
||||||
run: cargo test --profile ci --package nu_plugin_*
|
uses: actions-rs/cargo@v1.0.1
|
||||||
|
|
||||||
|
|
||||||
nu-coverage:
|
|
||||||
if: false
|
|
||||||
env:
|
|
||||||
NUSHELL_CARGO_TARGET: ci
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: true
|
|
||||||
matrix:
|
|
||||||
# disabled mac due to problems with merging coverage and similarity to linux
|
|
||||||
# disabled windows due to running out of disk space when having too many crates or tests
|
|
||||||
platform: [ubuntu-20.04] # windows-latest
|
|
||||||
rust:
|
|
||||||
- stable
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
|
||||||
with:
|
with:
|
||||||
rustflags: ""
|
command: test
|
||||||
|
args: --profile ci --package nu_plugin_*
|
||||||
- name: Install cargo-llvm-cov
|
|
||||||
uses: taiki-e/install-action@cargo-llvm-cov
|
|
||||||
|
|
||||||
- name: Tests
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
source <(cargo llvm-cov show-env --export-prefix) # Set the environment variables needed to get coverage.
|
|
||||||
cargo llvm-cov clean --workspace # Remove artifacts that may affect the coverage results.
|
|
||||||
cargo build --workspace --profile ci
|
|
||||||
cargo test --workspace --profile ci
|
|
||||||
cargo llvm-cov report --profile ci --lcov --output-path lcov.info
|
|
||||||
|
|
||||||
- name: Upload coverage reports to Codecov with GitHub Action
|
|
||||||
uses: codecov/codecov-action@v3
|
|
||||||
with:
|
|
||||||
files: lcov.info
|
|
||||||
|
41
.github/workflows/manual.yml
vendored
Normal file
41
.github/workflows/manual.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# This is a basic workflow that is manually triggered
|
||||||
|
# Don't run it unless you know what you are doing
|
||||||
|
|
||||||
|
name: Manual Workflow for Winget Submission
|
||||||
|
|
||||||
|
# Controls when the action will run. Workflow runs when manually triggered using the UI
|
||||||
|
# or API.
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
# Inputs the workflow accepts.
|
||||||
|
inputs:
|
||||||
|
ver:
|
||||||
|
# Friendly description to be shown in the UI instead of 'ver'
|
||||||
|
description: 'The nushell version to release'
|
||||||
|
# Default value if no value is explicitly provided
|
||||||
|
default: '0.66.0'
|
||||||
|
# Input has to be provided for the workflow to run
|
||||||
|
required: true
|
||||||
|
uri:
|
||||||
|
# Friendly description to be shown in the UI instead of 'uri'
|
||||||
|
description: 'The nushell windows .msi package URI to publish'
|
||||||
|
# Default value if no value is explicitly provided
|
||||||
|
default: 'https://github.com/nushell/nushell/releases/download/0.66.0/nu-0.66.0-x86_64-pc-windows-msvc.msi'
|
||||||
|
# Input has to be provided for the workflow to run
|
||||||
|
required: true
|
||||||
|
|
||||||
|
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||||
|
jobs:
|
||||||
|
# This workflow contains a single job
|
||||||
|
rls-winget-pkg:
|
||||||
|
name: Publish winget package manually
|
||||||
|
# The type of runner that the job will run on
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||||
|
steps:
|
||||||
|
# Runs commands using the runners shell
|
||||||
|
- name: Submit package to Windows Package Manager Community Repository Manually
|
||||||
|
run: |
|
||||||
|
iwr https://github.com/microsoft/winget-create/releases/download/v1.0.4.0/wingetcreate.exe -OutFile wingetcreate.exe
|
||||||
|
.\wingetcreate.exe update Nushell.Nushell -s -v ${{ github.event.inputs.ver }} -u ${{ github.event.inputs.uri }} -t ${{ secrets.NUSHELL_PAT }}
|
230
.github/workflows/nightly-build.yml
vendored
230
.github/workflows/nightly-build.yml
vendored
@@ -1,230 +0,0 @@
|
|||||||
#
|
|
||||||
# REF:
|
|
||||||
# 1. https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrixinclude
|
|
||||||
# 2. https://github.com/JasonEtco/create-an-issue
|
|
||||||
# 3. https://docs.github.com/en/actions/learn-github-actions/variables
|
|
||||||
# 4. https://github.com/actions/github-script
|
|
||||||
#
|
|
||||||
name: Nightly Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- nightly # Just for test purpose only with the nightly repo
|
|
||||||
# This schedule will run only from the default branch
|
|
||||||
schedule:
|
|
||||||
- cron: '15 1 * * *' # run at 01:15 AM UTC
|
|
||||||
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
prepare:
|
|
||||||
name: Prepare
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
# This job is required by the release job, so we should make it run both from Nushell repo and nightly repo
|
|
||||||
# if: github.repository == 'nushell/nightly'
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
if: github.repository == 'nushell/nightly'
|
|
||||||
with:
|
|
||||||
ref: main
|
|
||||||
fetch-depth: 0
|
|
||||||
# Configure PAT here: https://github.com/settings/tokens for the push operation in the following steps
|
|
||||||
token: ${{ secrets.WORKFLOW_TOKEN }}
|
|
||||||
|
|
||||||
- name: Setup Nushell
|
|
||||||
uses: hustcer/setup-nu@v3
|
|
||||||
if: github.repository == 'nushell/nightly'
|
|
||||||
with:
|
|
||||||
version: 0.81.0
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
# Synchronize the main branch of nightly repo with the main branch of Nushell official repo
|
|
||||||
- name: Prepare for Nightly Release
|
|
||||||
shell: nu {0}
|
|
||||||
if: github.repository == 'nushell/nightly'
|
|
||||||
run: |
|
|
||||||
cd $env.GITHUB_WORKSPACE
|
|
||||||
git checkout main
|
|
||||||
# We can't push if no user name and email are configured
|
|
||||||
git config user.name 'hustcer'
|
|
||||||
git config user.email 'hustcer@outlook.com'
|
|
||||||
git fetch origin main
|
|
||||||
git remote add src https://github.com/nushell/nushell.git
|
|
||||||
git fetch src main
|
|
||||||
# git pull --rebase src main
|
|
||||||
# All the changes will be overwritten by the upstream main branch
|
|
||||||
git reset --hard src/main
|
|
||||||
git push origin main -f
|
|
||||||
let sha_short = (git rev-parse --short src/main | str trim | str substring 0..7)
|
|
||||||
let tag_name = $'nightly-($sha_short)'
|
|
||||||
if (git ls-remote --tags origin $tag_name | is-empty) {
|
|
||||||
git tag -a $tag_name -m $'Nightly build from ($sha_short)'
|
|
||||||
git push origin --tags
|
|
||||||
}
|
|
||||||
|
|
||||||
release:
|
|
||||||
name: Release
|
|
||||||
needs: prepare
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
target:
|
|
||||||
- aarch64-apple-darwin
|
|
||||||
- x86_64-apple-darwin
|
|
||||||
- x86_64-pc-windows-msvc
|
|
||||||
- aarch64-pc-windows-msvc
|
|
||||||
- x86_64-unknown-linux-gnu
|
|
||||||
- x86_64-unknown-linux-musl
|
|
||||||
- aarch64-unknown-linux-gnu
|
|
||||||
- armv7-unknown-linux-gnueabihf
|
|
||||||
- riscv64gc-unknown-linux-gnu
|
|
||||||
extra: ['bin']
|
|
||||||
include:
|
|
||||||
- target: aarch64-apple-darwin
|
|
||||||
os: macos-latest
|
|
||||||
target_rustflags: ''
|
|
||||||
- target: x86_64-apple-darwin
|
|
||||||
os: macos-latest
|
|
||||||
target_rustflags: ''
|
|
||||||
- target: x86_64-pc-windows-msvc
|
|
||||||
extra: 'bin'
|
|
||||||
os: windows-latest
|
|
||||||
target_rustflags: ''
|
|
||||||
- target: x86_64-pc-windows-msvc
|
|
||||||
extra: msi
|
|
||||||
os: windows-latest
|
|
||||||
target_rustflags: ''
|
|
||||||
- target: aarch64-pc-windows-msvc
|
|
||||||
extra: 'bin'
|
|
||||||
os: windows-latest
|
|
||||||
target_rustflags: ''
|
|
||||||
- target: aarch64-pc-windows-msvc
|
|
||||||
extra: msi
|
|
||||||
os: windows-latest
|
|
||||||
target_rustflags: ''
|
|
||||||
- target: x86_64-unknown-linux-gnu
|
|
||||||
os: ubuntu-20.04
|
|
||||||
target_rustflags: ''
|
|
||||||
- target: x86_64-unknown-linux-musl
|
|
||||||
os: ubuntu-20.04
|
|
||||||
target_rustflags: ''
|
|
||||||
- target: aarch64-unknown-linux-gnu
|
|
||||||
os: ubuntu-20.04
|
|
||||||
target_rustflags: ''
|
|
||||||
- target: armv7-unknown-linux-gnueabihf
|
|
||||||
os: ubuntu-20.04
|
|
||||||
target_rustflags: ''
|
|
||||||
- target: riscv64gc-unknown-linux-gnu
|
|
||||||
os: ubuntu-20.04
|
|
||||||
target_rustflags: ''
|
|
||||||
|
|
||||||
runs-on: ${{matrix.os}}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
ref: main
|
|
||||||
|
|
||||||
- name: Update Rust Toolchain Target
|
|
||||||
run: |
|
|
||||||
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
|
||||||
with:
|
|
||||||
rustflags: ''
|
|
||||||
|
|
||||||
- name: Setup Nushell
|
|
||||||
uses: hustcer/setup-nu@v3
|
|
||||||
with:
|
|
||||||
version: 0.81.0
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Release Nu Binary
|
|
||||||
id: nu
|
|
||||||
run: nu .github/workflows/release-pkg.nu
|
|
||||||
env:
|
|
||||||
OS: ${{ matrix.os }}
|
|
||||||
REF: ${{ github.ref }}
|
|
||||||
TARGET: ${{ matrix.target }}
|
|
||||||
_EXTRA_: ${{ matrix.extra }}
|
|
||||||
TARGET_RUSTFLAGS: ${{ matrix.target_rustflags }}
|
|
||||||
|
|
||||||
- name: Create an Issue for Release Failure
|
|
||||||
if: ${{ failure() }}
|
|
||||||
uses: JasonEtco/create-an-issue@v2.9.1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
update_existing: true
|
|
||||||
search_existing: open
|
|
||||||
filename: .github/AUTO_ISSUE_TEMPLATE/nightly-build-fail.md
|
|
||||||
|
|
||||||
- name: Set Outputs of Short SHA
|
|
||||||
id: vars
|
|
||||||
run: |
|
|
||||||
echo "date=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
|
||||||
sha_short=$(git rev-parse --short HEAD)
|
|
||||||
echo "sha_short=${sha_short:0:7}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# REF: https://github.com/marketplace/actions/gh-release
|
|
||||||
# Create a release only in nushell/nightly repo
|
|
||||||
- name: Publish Archive
|
|
||||||
uses: softprops/action-gh-release@v0.1.13
|
|
||||||
if: ${{ startsWith(github.repository, 'nushell/nightly') }}
|
|
||||||
with:
|
|
||||||
draft: false
|
|
||||||
prerelease: true
|
|
||||||
name: Nu-nightly-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.sha_short }}
|
|
||||||
tag_name: nightly-${{ steps.vars.outputs.sha_short }}
|
|
||||||
body: |
|
|
||||||
This is a NIGHTLY build of Nushell.
|
|
||||||
It is NOT recommended for production use.
|
|
||||||
files: ${{ steps.nu.outputs.archive }}
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
name: Cleanup
|
|
||||||
# Should only run in nushell/nightly repo
|
|
||||||
if: github.repository == 'nushell/nightly'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
# Sleep for 30 minutes, waiting for the release to be published
|
|
||||||
- name: Waiting for Release
|
|
||||||
run: sleep 1800
|
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
ref: main
|
|
||||||
|
|
||||||
- name: Setup Nushell
|
|
||||||
uses: hustcer/setup-nu@v3
|
|
||||||
with:
|
|
||||||
version: 0.81.0
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
# Keep the last a few releases
|
|
||||||
- name: Delete Older Releases
|
|
||||||
shell: nu {0}
|
|
||||||
run: |
|
|
||||||
let KEEP_COUNT = 10
|
|
||||||
let deprecated = (http get https://api.github.com/repos/nushell/nightly/releases | sort-by -r created_at | select tag_name id | range $KEEP_COUNT..)
|
|
||||||
for release in $deprecated {
|
|
||||||
print $'Deleting tag ($release.tag_name)'
|
|
||||||
git push origin --delete $release.tag_name
|
|
||||||
print $'Deleting release ($release.tag_name)'
|
|
||||||
let delete_url = $'https://api.github.com/repos/nushell/nightly/releases/($release.id)'
|
|
||||||
let version = "X-GitHub-Api-Version: 2022-11-28"
|
|
||||||
let accept = "Accept: application/vnd.github+json"
|
|
||||||
let auth = "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
# http delete $delete_url -H $version -H $auth -H $accept
|
|
||||||
curl -L -X DELETE -H $accept -H $auth -H $version $delete_url
|
|
||||||
}
|
|
112
.github/workflows/release-pkg.nu
vendored
112
.github/workflows/release-pkg.nu
vendored
@@ -6,40 +6,6 @@
|
|||||||
# REF:
|
# REF:
|
||||||
# 1. https://github.com/volks73/cargo-wix
|
# 1. https://github.com/volks73/cargo-wix
|
||||||
|
|
||||||
# Instructions for manually creating an MSI for Winget Releases when they fail
|
|
||||||
# Added 2022-11-29 when Windows packaging wouldn't work
|
|
||||||
# Updated again on 2023-02-23 because msis are still failing validation
|
|
||||||
# To run this manual for windows here are the steps I take
|
|
||||||
# checkout the release you want to publish
|
|
||||||
# 1. git checkout 0.76.0
|
|
||||||
# unset CARGO_TARGET_DIR if set (I have to do this in the parent shell to get it to work)
|
|
||||||
# 2. $env:CARGO_TARGET_DIR = ""
|
|
||||||
# 2. hide-env CARGO_TARGET_DIR
|
|
||||||
# 3. let-env TARGET = 'x86_64-pc-windows-msvc'
|
|
||||||
# 4. let-env TARGET_RUSTFLAGS = ''
|
|
||||||
# 5. let-env GITHUB_WORKSPACE = 'C:\Users\dschroeder\source\repos\forks\nushell'
|
|
||||||
# 6. let-env GITHUB_OUTPUT = 'C:\Users\dschroeder\source\repos\forks\nushell\output\out.txt'
|
|
||||||
# 7. let-env OS = 'windows-latest'
|
|
||||||
# make sure 7z.exe is in your path https://www.7-zip.org/download.html
|
|
||||||
# 8. let-env Path = ($env.Path | append 'c:\apps\7-zip')
|
|
||||||
# make sure aria2c.exe is in your path https://github.com/aria2/aria2
|
|
||||||
# 9. let-env Path = ($env.Path | append 'c:\path\to\aria2c')
|
|
||||||
# make sure you have the wixtools installed https://wixtoolset.org/
|
|
||||||
# 10. let-env Path = ($env.Path | append 'C:\Users\dschroeder\AppData\Local\tauri\WixTools')
|
|
||||||
# You need to run the release-pkg twice. The first pass, with _EXTRA_ as 'bin', makes the output
|
|
||||||
# folder and builds everything. The second pass, that generates the msi file, with _EXTRA_ as 'msi'
|
|
||||||
# 11. let-env _EXTRA_ = 'bin'
|
|
||||||
# 12. source .github\workflows\release-pkg.nu
|
|
||||||
# 13. cd ..
|
|
||||||
# 14. let-env _EXTRA_ = 'msi'
|
|
||||||
# 15. source .github\workflows\release-pkg.nu
|
|
||||||
# After msi is generated, you have to update winget-pkgs repo, you'll need to patch the release
|
|
||||||
# by deleting the existing msi and uploading this new msi. Then you'll need to update the hash
|
|
||||||
# on the winget-pkgs PR. To generate the hash, run this command
|
|
||||||
# 16. open target\wix\nu-0.74.0-x86_64-pc-windows-msvc.msi | hash sha256
|
|
||||||
# Then, just take the output and put it in the winget-pkgs PR for the hash on the msi
|
|
||||||
|
|
||||||
|
|
||||||
# The main binary file to be released
|
# The main binary file to be released
|
||||||
let bin = 'nu'
|
let bin = 'nu'
|
||||||
let os = $env.OS
|
let os = $env.OS
|
||||||
@@ -50,48 +16,38 @@ let flags = $env.TARGET_RUSTFLAGS
|
|||||||
let dist = $'($env.GITHUB_WORKSPACE)/output'
|
let dist = $'($env.GITHUB_WORKSPACE)/output'
|
||||||
let version = (open Cargo.toml | get package.version)
|
let version = (open Cargo.toml | get package.version)
|
||||||
|
|
||||||
print $'Debugging info:'
|
$'Debugging info:'
|
||||||
print { version: $version, bin: $bin, os: $os, target: $target, src: $src, flags: $flags, dist: $dist }; hr-line -b
|
print { version: $version, bin: $bin, os: $os, target: $target, src: $src, flags: $flags, dist: $dist }; hr-line -b
|
||||||
|
|
||||||
# $env
|
# $env
|
||||||
|
|
||||||
let USE_UBUNTU = 'ubuntu-20.04'
|
let USE_UBUNTU = 'ubuntu-20.04'
|
||||||
|
|
||||||
print $'(char nl)Packaging ($bin) v($version) for ($target) in ($src)...'; hr-line -b
|
$'(char nl)Packaging ($bin) v($version) for ($target) in ($src)...'; hr-line -b
|
||||||
if not ('Cargo.lock' | path exists) { cargo generate-lockfile }
|
if not ('Cargo.lock' | path exists) { cargo generate-lockfile }
|
||||||
|
|
||||||
print $'Start building ($bin)...'; hr-line
|
$'Start building ($bin)...'; hr-line
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# Build for Ubuntu and macOS
|
# Build for Ubuntu and macOS
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
if $os in [$USE_UBUNTU, 'macos-latest'] {
|
if $os in [$USE_UBUNTU, 'macos-latest'] {
|
||||||
if $os == $USE_UBUNTU {
|
if $os == $USE_UBUNTU {
|
||||||
sudo apt update
|
|
||||||
sudo apt-get install libxcb-composite0-dev -y
|
sudo apt-get install libxcb-composite0-dev -y
|
||||||
}
|
}
|
||||||
match $target {
|
if $target == 'aarch64-unknown-linux-gnu' {
|
||||||
'aarch64-unknown-linux-gnu' => {
|
sudo apt-get install gcc-aarch64-linux-gnu -y
|
||||||
sudo apt-get install gcc-aarch64-linux-gnu -y
|
let-env CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER = 'aarch64-linux-gnu-gcc'
|
||||||
let-env CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER = 'aarch64-linux-gnu-gcc'
|
cargo-build-nu $flags
|
||||||
cargo-build-nu $flags
|
} else if $target == 'armv7-unknown-linux-gnueabihf' {
|
||||||
}
|
sudo apt-get install pkg-config gcc-arm-linux-gnueabihf -y
|
||||||
'riscv64gc-unknown-linux-gnu' => {
|
let-env CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = 'arm-linux-gnueabihf-gcc'
|
||||||
sudo apt-get install gcc-riscv64-linux-gnu -y
|
cargo-build-nu $flags
|
||||||
let-env CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER = 'riscv64-linux-gnu-gcc'
|
} else {
|
||||||
cargo-build-nu $flags
|
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
|
||||||
}
|
# Actually just for x86_64-unknown-linux-musl target
|
||||||
'armv7-unknown-linux-gnueabihf' => {
|
if $os == $USE_UBUNTU { sudo apt install musl-tools -y }
|
||||||
sudo apt-get install pkg-config gcc-arm-linux-gnueabihf -y
|
cargo-build-nu $flags
|
||||||
let-env CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = 'arm-linux-gnueabihf-gcc'
|
|
||||||
cargo-build-nu $flags
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
|
|
||||||
# Actually just for x86_64-unknown-linux-musl target
|
|
||||||
if $os == $USE_UBUNTU { sudo apt install musl-tools -y }
|
|
||||||
cargo-build-nu $flags
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +55,6 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
# Build for Windows without static-link-openssl feature
|
# Build for Windows without static-link-openssl feature
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
if $os in ['windows-latest'] {
|
if $os in ['windows-latest'] {
|
||||||
# let-env CARGO_BUILD_TARGET = $target
|
|
||||||
if ($flags | str trim | is-empty) {
|
if ($flags | str trim | is-empty) {
|
||||||
cargo build --release --all --target $target
|
cargo build --release --all --target $target
|
||||||
} else {
|
} else {
|
||||||
@@ -113,36 +68,31 @@ if $os in ['windows-latest'] {
|
|||||||
let suffix = if $os == 'windows-latest' { '.exe' }
|
let suffix = if $os == 'windows-latest' { '.exe' }
|
||||||
# nu, nu_plugin_* were all included
|
# nu, nu_plugin_* were all included
|
||||||
let executable = $'target/($target)/release/($bin)*($suffix)'
|
let executable = $'target/($target)/release/($bin)*($suffix)'
|
||||||
print $'Current executable file: ($executable)'
|
$'Current executable file: ($executable)'
|
||||||
|
|
||||||
cd $src; mkdir $dist;
|
cd $src; mkdir $dist;
|
||||||
rm -rf $'target/($target)/release/*.d' $'target/($target)/release/nu_pretty_hex*'
|
rm -rf $'target/($target)/release/*.d' $'target/($target)/release/nu_pretty_hex*'
|
||||||
print $'(char nl)All executable files:'; hr-line
|
$'(char nl)All executable files:'; hr-line
|
||||||
# We have to use `print` here to make sure the command output is displayed
|
ls -f $executable
|
||||||
print (ls -f $executable); sleep 1sec
|
|
||||||
|
|
||||||
print $'(char nl)Copying release files...'; hr-line
|
$'(char nl)Copying release files...'; hr-line
|
||||||
"To use Nu plugins, use the register command to tell Nu where to find the plugin. For example:
|
cp -v README.release.txt $'($dist)/README.txt'
|
||||||
|
|
||||||
> register ./nu_plugin_query" | save $'($dist)/README.txt'
|
|
||||||
[LICENSE $executable] | each {|it| cp -rv $it $dist } | flatten
|
[LICENSE $executable] | each {|it| cp -rv $it $dist } | flatten
|
||||||
# Sleep a few seconds to make sure the cp process finished successfully
|
|
||||||
sleep 3sec
|
|
||||||
|
|
||||||
print $'(char nl)Check binary release version detail:'; hr-line
|
$'(char nl)Check binary release version detail:'; hr-line
|
||||||
let ver = if $os == 'windows-latest' {
|
let ver = if $os == 'windows-latest' {
|
||||||
(do -i { .\output\nu.exe -c 'version' }) | str join
|
(do -i { ./output/nu.exe -c 'version' }) | str join
|
||||||
} else {
|
} else {
|
||||||
(do -i { ./output/nu -c 'version' }) | str join
|
(do -i { ./output/nu -c 'version' }) | str join
|
||||||
}
|
}
|
||||||
if ($ver | str trim | is-empty) {
|
if ($ver | str trim | is-empty) {
|
||||||
print $'(ansi r)Incompatible nu binary...(ansi reset)'
|
$'(ansi r)Incompatible nu binary...(ansi reset)'
|
||||||
} else { print $ver }
|
} else { $ver }
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# Create a release archive and send it to output for the following steps
|
# Create a release archive and send it to output for the following steps
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
cd $dist; print $'(char nl)Creating release archive...'; hr-line
|
cd $dist; $'(char nl)Creating release archive...'; hr-line
|
||||||
if $os in [$USE_UBUNTU, 'macos-latest'] {
|
if $os in [$USE_UBUNTU, 'macos-latest'] {
|
||||||
|
|
||||||
let files = (ls | get name)
|
let files = (ls | get name)
|
||||||
@@ -152,7 +102,7 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
mkdir $dest
|
mkdir $dest
|
||||||
$files | each {|it| mv $it $dest } | ignore
|
$files | each {|it| mv $it $dest } | ignore
|
||||||
|
|
||||||
print $'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls $dest
|
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls $dest
|
||||||
|
|
||||||
tar -czf $archive $dest
|
tar -czf $archive $dest
|
||||||
print $'archive: ---> ($archive)'; ls $archive
|
print $'archive: ---> ($archive)'; ls $archive
|
||||||
@@ -163,7 +113,7 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
|
|
||||||
let releaseStem = $'($bin)-($version)-($target)'
|
let releaseStem = $'($bin)-($version)-($target)'
|
||||||
|
|
||||||
print $'(char nl)Download less related stuffs...'; hr-line
|
$'(char nl)Download less related stuffs...'; hr-line
|
||||||
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v608/less.exe -o less.exe
|
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v608/less.exe -o less.exe
|
||||||
aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
|
aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
|
||||||
|
|
||||||
@@ -171,18 +121,18 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
if (get-env _EXTRA_) == 'msi' {
|
if (get-env _EXTRA_) == 'msi' {
|
||||||
|
|
||||||
let wixRelease = $'($src)/target/wix/($releaseStem).msi'
|
let wixRelease = $'($src)/target/wix/($releaseStem).msi'
|
||||||
print $'(char nl)Start creating Windows msi package...'
|
$'(char nl)Start creating Windows msi package...'
|
||||||
cd $src; hr-line
|
cd $src; hr-line
|
||||||
# Wix need the binaries be stored in target/release/
|
# Wix need the binaries be stored in target/release/
|
||||||
cp -r $'($dist)/*' target/release/
|
cp -r $'($dist)/*' target/release/
|
||||||
cargo install cargo-wix --version 0.3.4
|
cargo install cargo-wix --version 0.3.3
|
||||||
cargo wix --no-build --nocapture --package nu --output $wixRelease
|
cargo wix --no-build --nocapture --package nu --output $wixRelease
|
||||||
print $'archive: ---> ($wixRelease)';
|
print $'archive: ---> ($wixRelease)';
|
||||||
echo $"archive=($wixRelease)" | save --append $env.GITHUB_OUTPUT
|
echo $"archive=($wixRelease)" | save --append $env.GITHUB_OUTPUT
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
print $'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls
|
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls
|
||||||
let archive = $'($dist)/($releaseStem).zip'
|
let archive = $'($dist)/($releaseStem).zip'
|
||||||
7z a $archive *
|
7z a $archive *
|
||||||
print $'archive: ---> ($archive)';
|
print $'archive: ---> ($archive)';
|
||||||
|
30
.github/workflows/release.yml
vendored
30
.github/workflows/release.yml
vendored
@@ -23,10 +23,10 @@ jobs:
|
|||||||
- aarch64-apple-darwin
|
- aarch64-apple-darwin
|
||||||
- x86_64-apple-darwin
|
- x86_64-apple-darwin
|
||||||
- x86_64-pc-windows-msvc
|
- x86_64-pc-windows-msvc
|
||||||
- aarch64-pc-windows-msvc
|
|
||||||
- x86_64-unknown-linux-gnu
|
- x86_64-unknown-linux-gnu
|
||||||
- x86_64-unknown-linux-musl
|
- x86_64-unknown-linux-musl
|
||||||
- aarch64-unknown-linux-gnu
|
- aarch64-unknown-linux-gnu
|
||||||
|
- armv7-unknown-linux-gnueabihf
|
||||||
extra: ['bin']
|
extra: ['bin']
|
||||||
include:
|
include:
|
||||||
- target: aarch64-apple-darwin
|
- target: aarch64-apple-darwin
|
||||||
@@ -43,14 +43,6 @@ jobs:
|
|||||||
extra: msi
|
extra: msi
|
||||||
os: windows-latest
|
os: windows-latest
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
- target: aarch64-pc-windows-msvc
|
|
||||||
extra: 'bin'
|
|
||||||
os: windows-latest
|
|
||||||
target_rustflags: ''
|
|
||||||
- target: aarch64-pc-windows-msvc
|
|
||||||
extra: msi
|
|
||||||
os: windows-latest
|
|
||||||
target_rustflags: ''
|
|
||||||
- target: x86_64-unknown-linux-gnu
|
- target: x86_64-unknown-linux-gnu
|
||||||
os: ubuntu-20.04
|
os: ubuntu-20.04
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
@@ -60,25 +52,27 @@ jobs:
|
|||||||
- target: aarch64-unknown-linux-gnu
|
- target: aarch64-unknown-linux-gnu
|
||||||
os: ubuntu-20.04
|
os: ubuntu-20.04
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
|
- target: armv7-unknown-linux-gnueabihf
|
||||||
|
os: ubuntu-20.04
|
||||||
|
target_rustflags: ''
|
||||||
|
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3.1.0
|
||||||
|
|
||||||
- name: Update Rust Toolchain Target
|
- name: Install Rust Toolchain Components
|
||||||
run: |
|
uses: actions-rs/toolchain@v1.0.6
|
||||||
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
|
||||||
with:
|
with:
|
||||||
rustflags: ''
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3
|
uses: hustcer/setup-nu@v3
|
||||||
with:
|
with:
|
||||||
version: 0.81.0
|
version: 0.71.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
28
.github/workflows/stale.yml
vendored
Normal file
28
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
name: 'Close stale issues and PRs'
|
||||||
|
#on: [workflow_dispatch]
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '30 1 * * *'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v3
|
||||||
|
with:
|
||||||
|
#debug-only: true
|
||||||
|
ascending: true
|
||||||
|
operations-per-run: 520
|
||||||
|
enable-statistics: true
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
close-issue-message: 'This issue has been marked stale for more than 100000 days without activity. Closing this issue, but if you find that the issue is still valid, please reopen.'
|
||||||
|
close-pr-message: 'This PR has been marked stale for more than 100 days without activity. Closing this PR, but if you are still working on it, please reopen.'
|
||||||
|
days-before-issue-stale: 90
|
||||||
|
days-before-pr-stale: 45
|
||||||
|
days-before-issue-close: 100000
|
||||||
|
days-before-pr-close: 100
|
||||||
|
exempt-issue-labels: 'exempt,keep'
|
13
.github/workflows/typos.yml
vendored
13
.github/workflows/typos.yml
vendored
@@ -1,13 +0,0 @@
|
|||||||
name: Typos
|
|
||||||
on: [pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
run:
|
|
||||||
name: Spell Check with Typos
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout Actions Repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Check spelling
|
|
||||||
uses: crate-ci/typos@master
|
|
21
.github/workflows/winget-submission.yml
vendored
21
.github/workflows/winget-submission.yml
vendored
@@ -1,14 +1,9 @@
|
|||||||
name: Submit Nushell package to Windows Package Manager Community Repository
|
name: Submit Nushell package to Windows Package Manager Community Repository
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
|
||||||
types: [released]
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
release:
|
||||||
tag_name:
|
types: [published]
|
||||||
description: 'Specific tag name'
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
@@ -17,10 +12,8 @@ jobs:
|
|||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Submit package to Windows Package Manager Community Repository
|
- name: Submit package to Windows Package Manager Community Repository
|
||||||
uses: vedantmgoyal2009/winget-releaser@v2
|
run: |
|
||||||
with:
|
iwr https://github.com/microsoft/winget-create/releases/download/v1.0.4.0/wingetcreate.exe -OutFile wingetcreate.exe
|
||||||
identifier: Nushell.Nushell
|
$github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json
|
||||||
version: ${{ inputs.tag_name || github.event.release.tag_name }}
|
$installerUrl = $github.release.assets | Where-Object -Property name -match 'windows-msvc.msi' | Select -ExpandProperty browser_download_url -First 1
|
||||||
release-tag: ${{ inputs.tag_name || github.event.release.tag_name }}
|
.\wingetcreate.exe update Nushell.Nushell -s -v $github.release.tag_name -u $installerUrl -t ${{ secrets.NUSHELL_PAT }}
|
||||||
token: ${{ secrets.NUSHELL_PAT }}
|
|
||||||
fork-user: fdncred
|
|
||||||
|
11
.gitignore
vendored
11
.gitignore
vendored
@@ -22,9 +22,6 @@ debian/nu/
|
|||||||
# VSCode's IDE items
|
# VSCode's IDE items
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|
||||||
# JetBrains' Fleet IDE
|
|
||||||
.fleet/*
|
|
||||||
|
|
||||||
# Visual Studio Extension SourceGear Rust items
|
# Visual Studio Extension SourceGear Rust items
|
||||||
VSWorkspaceSettings.json
|
VSWorkspaceSettings.json
|
||||||
unstable_cargo_features.txt
|
unstable_cargo_features.txt
|
||||||
@@ -42,11 +39,3 @@ tarpaulin-report.html
|
|||||||
*.rsproj
|
*.rsproj
|
||||||
*.rsproj.user
|
*.rsproj.user
|
||||||
*.sln
|
*.sln
|
||||||
*.code-workspace
|
|
||||||
|
|
||||||
# direnv
|
|
||||||
.direnv/
|
|
||||||
.envrc
|
|
||||||
|
|
||||||
# pre-commit-hooks
|
|
||||||
.pre-commit-config.yaml
|
|
||||||
|
13
.typos.toml
13
.typos.toml
@@ -1,13 +0,0 @@
|
|||||||
[files]
|
|
||||||
extend-exclude = ["crates/nu-command/tests/commands/table.rs", "*.tsv", "*.json", "*.txt"]
|
|
||||||
|
|
||||||
[default.extend-words]
|
|
||||||
# Ignore false-positives
|
|
||||||
nd = "nd"
|
|
||||||
fo = "fo"
|
|
||||||
ons = "ons"
|
|
||||||
ba = "ba"
|
|
||||||
Plasticos = "Plasticos"
|
|
||||||
IIF = "IIF"
|
|
||||||
numer = "numer"
|
|
||||||
ratatui = "ratatui"
|
|
127
CONTRIBUTING.md
127
CONTRIBUTING.md
@@ -59,12 +59,7 @@ The most comprehensive test suite we have is the `nu-test-support` crate. For te
|
|||||||
- Run Clippy on Nushell:
|
- Run Clippy on Nushell:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err
|
cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
||||||
```
|
|
||||||
or via the `toolkit.nu` command:
|
|
||||||
```shell
|
|
||||||
use toolkit.nu clippy
|
|
||||||
clippy
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run all tests:
|
- Run all tests:
|
||||||
@@ -73,17 +68,6 @@ The most comprehensive test suite we have is the `nu-test-support` crate. For te
|
|||||||
cargo test --workspace
|
cargo test --workspace
|
||||||
```
|
```
|
||||||
|
|
||||||
along with dataframe tests
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cargo test --workspace --features=dataframe
|
|
||||||
```
|
|
||||||
or via the `toolkit.nu` command:
|
|
||||||
```shell
|
|
||||||
use toolkit.nu test
|
|
||||||
test
|
|
||||||
```
|
|
||||||
|
|
||||||
- Run all tests for a specific command
|
- Run all tests for a specific command
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@@ -95,30 +79,12 @@ The most comprehensive test suite we have is the `nu-test-support` crate. For te
|
|||||||
```shell
|
```shell
|
||||||
cargo fmt --all -- --check
|
cargo fmt --all -- --check
|
||||||
```
|
```
|
||||||
or via the `toolkit.nu` command:
|
|
||||||
```shell
|
|
||||||
use toolkit.nu fmt
|
|
||||||
fmt --check
|
|
||||||
```
|
|
||||||
|
|
||||||
- Format the code in the project
|
- Format the code in the project
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo fmt --all
|
cargo fmt --all
|
||||||
```
|
```
|
||||||
or via the `toolkit.nu` command:
|
|
||||||
```shell
|
|
||||||
use toolkit.nu fmt
|
|
||||||
fmt
|
|
||||||
```
|
|
||||||
|
|
||||||
- Set up `git` hooks to check formatting and run `clippy` before committing and pushing:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
use toolkit.nu setup-git-hooks
|
|
||||||
setup-git-hooks
|
|
||||||
```
|
|
||||||
_Unfortunately, this hook isn't available on Windows._
|
|
||||||
|
|
||||||
### Debugging Tips
|
### Debugging Tips
|
||||||
|
|
||||||
@@ -133,94 +99,3 @@ The most comprehensive test suite we have is the `nu-test-support` crate. For te
|
|||||||
cargo run --release -- --log-level trace --log-target file
|
cargo run --release -- --log-level trace --log-target file
|
||||||
open $"($nu.temp-path)/nu-($nu.pid).log"
|
open $"($nu.temp-path)/nu-($nu.pid).log"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Git etiquette
|
|
||||||
|
|
||||||
As nushell thrives on its broad base of volunteer contributors and maintainers with different backgrounds we have a few guidelines for how we best utilize git and GitHub for our contributions. We strive to balance three goals with those recommendations:
|
|
||||||
|
|
||||||
1. The **volunteer maintainers and contributors** can easily follow the changes you propose, gauge the impact, and come to help you or make a decision.
|
|
||||||
2. **You as a contributor** can focus most of your time on improving the quality of the nushell project and contributing your expertise to the code or documentation.
|
|
||||||
3. Making sure we can trace back *why* decisions were made in the past.
|
|
||||||
This includes discarded approaches. Also we want to quickly identify regressions and fix when something broke.
|
|
||||||
|
|
||||||
### How we merge PRs
|
|
||||||
|
|
||||||
In general the maintainers **squash** all changes of your PR into a single commit when merging.
|
|
||||||
|
|
||||||
This keeps a clean enough linear history, while not forcing you to conform to a too strict style while iterating in your PR or fixing small problems. As an added benefit the commits on the `main` branch are tied to the discussion that happened in the PR through their `#1234` issue number.
|
|
||||||
|
|
||||||
> **Note**
|
|
||||||
> **Pro advice:** In some circumstances, we can agree on rebase-merging a particularly large but connected PR as a series of atomic commits onto the `main` branch to ensure we can more easily revert or bisect particular aspects.
|
|
||||||
|
|
||||||
### A good PR makes a change!
|
|
||||||
|
|
||||||
As a result of this PR-centric strategy and the general goal that the reviewers should easily understand your change, the **PR title and description matters** a great deal!
|
|
||||||
|
|
||||||
Make sure your description is **concise** but contains all relevant information and context.
|
|
||||||
This means demonstrating what changes, ideally through nushell code or output **examples**.
|
|
||||||
Furthermore links to technical documentation or instructions for folks that want to play with your change make the review process much easier.
|
|
||||||
|
|
||||||
> **Note**
|
|
||||||
> Try to follow the suggestions in our PR message template to make sure we can quickly focus on the technical merits and impact on the users.
|
|
||||||
|
|
||||||
#### A PR should limit itself to a single functional change or related set of same changes.
|
|
||||||
|
|
||||||
Mixing different changes in the same PR will make the review process much harder. A PR might get stuck on one aspect while we would actually like to land another change. Furthermore, if we are forced to revert a change, mixing and matching different aspects makes fixing bugs or regressions much harder.
|
|
||||||
|
|
||||||
Thus, please try to **separate out unrelated changes**!
|
|
||||||
**Don't** mix unrelated refactors with a potentially contested change.
|
|
||||||
Stylistic fixes and housekeeping can be bundled up into singular PRs.
|
|
||||||
|
|
||||||
#### Guidelines for the PR title
|
|
||||||
|
|
||||||
The PR title should be concise but contain everything for a contributor to know if they should help out in the review of this particular change.
|
|
||||||
|
|
||||||
**DON'T**
|
|
||||||
- `Update file/in/some/deeply/nested/path.rs`
|
|
||||||
- Why are you making this change?
|
|
||||||
- `Fix 2134`
|
|
||||||
- What has to be fixed?
|
|
||||||
- Hard to follow when not online on GitHub.
|
|
||||||
- ``Ignore `~` expansion``
|
|
||||||
- In what context should this change take effect?
|
|
||||||
- `[feature] refactor the whole parser and also make nushell indentation-sensitive, upgrade to using Cpython. Let me know what you think!`
|
|
||||||
- Be concise
|
|
||||||
- Maybe break up into smaller commits or PRs if the title already appears too long?
|
|
||||||
|
|
||||||
**DO**
|
|
||||||
- Mention the nushell feature or command that is affected.
|
|
||||||
- ``Fix URL parsing in `http get` (issue #1234)``
|
|
||||||
- You can mention the issue number if other context is there.
|
|
||||||
- In general, mention all related issues in the description to crosslink (e.g. `Fixes #1234`, `Closes #6789`)
|
|
||||||
- For internal changes mention the area or symbols affected if it helps to clarify
|
|
||||||
- ``Factor out `quote_string()` from parser to reuse in `explore` ``
|
|
||||||
|
|
||||||
### Review process / Merge conflicts
|
|
||||||
|
|
||||||
> **Note**
|
|
||||||
> Keep in mind that the maintainers are volunteers that need to allocate their attention to several different areas and active PRs. We will try to get back to you as soon as possible.
|
|
||||||
|
|
||||||
You can help us to make the review process a smooth experience:
|
|
||||||
- Testing:
|
|
||||||
- We generally review in detail after all the tests pass. Let us know if there is a problem you want to discuss to fix a test failure or forces us to accept a breaking change.
|
|
||||||
- If you fix a bug, it is highly recommended that you add a test that reproduces the original issue/panic in a minimal form.
|
|
||||||
- In general, added tests help us to understand which assumptions go into a particular addition/change.
|
|
||||||
- Try to also test corner cases where those assumptions might break. This can be more valuable than simply adding many similar tests.
|
|
||||||
- Commit history inside a PR during code review:
|
|
||||||
- Good **atomic commits** can help follow larger changes, but we are not pedantic.
|
|
||||||
- We don't shame fixup commits while you try to figure out a problem. They can help others see what you tried and what didn't work. (see our [squash policy](#how-we-merge-prs))
|
|
||||||
- During active review constant **force pushing** just to amend changes can be confusing!
|
|
||||||
- GitHub's UI presents reviewers with less options to compare diffs
|
|
||||||
- fetched branches for experimentation become invalid!
|
|
||||||
- the notification a maintainer receives has a low signal-to-noise ratio
|
|
||||||
- Git pros *can* use their judgement to rebase/squash to clean up the history *if it aids the understanding* of a larger change during review
|
|
||||||
- Merge conflicts:
|
|
||||||
- In general you should take care of resolving merge conflicts.
|
|
||||||
- Use your judgement whether to `git merge main` or to `git rebase main`
|
|
||||||
- Choose what simplifies having confidence in the conflict resolution and the review. **Merge commits in your branch are OK** in the squash model.
|
|
||||||
- Feel free to notify your reviewers or affected PR authors if your change might cause larger conflicts with another change.
|
|
||||||
- During the rollup of multiple PRs, we may choose to resolve merge conflicts and CI failures ourselves. (Allow maintainers to push to your branch to enable us to do this quickly.)
|
|
||||||
|
|
||||||
### License
|
|
||||||
|
|
||||||
We use the [MIT License](https://github.com/nushell/nushell/blob/main/LICENSE) in all of our Nushell projects. If you are including or referencing a crate that uses the [GPL License](https://www.gnu.org/licenses/gpl-3.0.en.html#license-text) unfortunately we will not be able to accept your PR.
|
|
||||||
|
3000
Cargo.lock
generated
3000
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
138
Cargo.toml
138
Cargo.toml
@@ -1,17 +1,16 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["The Nushell Project Developers"]
|
authors = ["The Nushell Project Developers"]
|
||||||
build = "scripts/build.rs"
|
|
||||||
default-run = "nu"
|
default-run = "nu"
|
||||||
description = "A new type of shell"
|
description = "A new type of shell"
|
||||||
documentation = "https://www.nushell.sh/book/"
|
documentation = "https://www.nushell.sh/book/"
|
||||||
edition = "2021"
|
edition = "2018"
|
||||||
exclude = ["images"]
|
exclude = ["images"]
|
||||||
homepage = "https://www.nushell.sh"
|
homepage = "https://www.nushell.sh"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu"
|
name = "nu"
|
||||||
repository = "https://github.com/nushell/nushell"
|
repository = "https://github.com/nushell/nushell"
|
||||||
rust-version = "1.60"
|
rust-version = "1.60"
|
||||||
version = "0.82.0"
|
version = "0.72.1"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@@ -28,10 +27,6 @@ members = [
|
|||||||
"crates/nu-engine",
|
"crates/nu-engine",
|
||||||
"crates/nu-parser",
|
"crates/nu-parser",
|
||||||
"crates/nu-system",
|
"crates/nu-system",
|
||||||
"crates/nu-cmd-base",
|
|
||||||
"crates/nu-cmd-extra",
|
|
||||||
"crates/nu-cmd-lang",
|
|
||||||
"crates/nu-cmd-dataframe",
|
|
||||||
"crates/nu-command",
|
"crates/nu-command",
|
||||||
"crates/nu-protocol",
|
"crates/nu-protocol",
|
||||||
"crates/nu-plugin",
|
"crates/nu-plugin",
|
||||||
@@ -40,105 +35,85 @@ members = [
|
|||||||
"crates/nu_plugin_example",
|
"crates/nu_plugin_example",
|
||||||
"crates/nu_plugin_query",
|
"crates/nu_plugin_query",
|
||||||
"crates/nu_plugin_custom_values",
|
"crates/nu_plugin_custom_values",
|
||||||
"crates/nu_plugin_formats",
|
|
||||||
"crates/nu-std",
|
|
||||||
"crates/nu-utils",
|
"crates/nu-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cli = { path = "./crates/nu-cli", version = "0.82.0" }
|
chrono = { version = "0.4.23", features = ["serde"] }
|
||||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.82.0" }
|
crossterm = "0.24.0"
|
||||||
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.82.0" }
|
ctrlc = "3.2.1"
|
||||||
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.82.0" }
|
|
||||||
nu-cmd-dataframe = { path = "./crates/nu-cmd-dataframe", version = "0.82.0", optional = true }
|
|
||||||
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.82.0", optional = true }
|
|
||||||
nu-command = { path = "./crates/nu-command", version = "0.82.0" }
|
|
||||||
nu-engine = { path = "./crates/nu-engine", version = "0.82.0" }
|
|
||||||
nu-explore = { path = "./crates/nu-explore", version = "0.82.0" }
|
|
||||||
nu-json = { path = "./crates/nu-json", version = "0.82.0" }
|
|
||||||
nu-parser = { path = "./crates/nu-parser", version = "0.82.0" }
|
|
||||||
nu-path = { path = "./crates/nu-path", version = "0.82.0" }
|
|
||||||
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.82.0" }
|
|
||||||
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.82.0" }
|
|
||||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.82.0" }
|
|
||||||
nu-system = { path = "./crates/nu-system", version = "0.82.0" }
|
|
||||||
nu-table = { path = "./crates/nu-table", version = "0.82.0" }
|
|
||||||
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.82.0" }
|
|
||||||
nu-std = { path = "./crates/nu-std", version = "0.82.0" }
|
|
||||||
nu-utils = { path = "./crates/nu-utils", version = "0.82.0" }
|
|
||||||
nu-ansi-term = "0.47.0"
|
|
||||||
reedline = { version = "0.21.0", features = ["bashisms", "sqlite"]}
|
|
||||||
|
|
||||||
mimalloc = { version = "0.1.37", default-features = false, optional = true}
|
|
||||||
|
|
||||||
crossterm = "0.26"
|
|
||||||
ctrlc = "3.4"
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
miette = { version = "5.9", features = ["fancy-no-backtrace"] }
|
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
|
||||||
serde_json = "1.0"
|
nu-ansi-term = "0.46.0"
|
||||||
simplelog = "0.12"
|
nu-cli = { path="./crates/nu-cli", version = "0.72.1" }
|
||||||
time = "0.3"
|
nu-color-config = { path = "./crates/nu-color-config", version = "0.72.1" }
|
||||||
|
nu-command = { path="./crates/nu-command", version = "0.72.1" }
|
||||||
|
nu-engine = { path="./crates/nu-engine", version = "0.72.1" }
|
||||||
|
nu-json = { path="./crates/nu-json", version = "0.72.1" }
|
||||||
|
nu-parser = { path="./crates/nu-parser", version = "0.72.1" }
|
||||||
|
nu-path = { path="./crates/nu-path", version = "0.72.1" }
|
||||||
|
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.72.1" }
|
||||||
|
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.72.1" }
|
||||||
|
nu-protocol = { path = "./crates/nu-protocol", version = "0.72.1" }
|
||||||
|
nu-system = { path = "./crates/nu-system", version = "0.72.1" }
|
||||||
|
nu-table = { path = "./crates/nu-table", version = "0.72.1" }
|
||||||
|
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.72.1" }
|
||||||
|
nu-utils = { path = "./crates/nu-utils", version = "0.72.1" }
|
||||||
|
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
||||||
|
|
||||||
|
rayon = "1.5.1"
|
||||||
|
is_executable = "1.0.1"
|
||||||
|
simplelog = "0.12.0"
|
||||||
|
time = "0.3.12"
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
||||||
# Our dependencies don't use OpenSSL on Windows
|
# Our dependencies don't use OpenSSL on Windows
|
||||||
openssl = { version = "0.10", features = ["vendored"], optional = true }
|
openssl = { version = "0.10.38", features = ["vendored"], optional = true }
|
||||||
signal-hook = { version = "0.3", default-features = false }
|
signal-hook = { version = "0.3.14", default-features = false }
|
||||||
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.build-dependencies]
|
[target.'cfg(windows)'.build-dependencies]
|
||||||
winresource = "0.1"
|
winres = "0.1"
|
||||||
|
|
||||||
[target.'cfg(target_family = "unix")'.dependencies]
|
[target.'cfg(target_family = "unix")'.dependencies]
|
||||||
nix = { version = "0.26", default-features = false, features = [
|
nix = { version = "0.25", default-features = false, features = ["signal", "process", "fs", "term"]}
|
||||||
"signal",
|
|
||||||
"process",
|
|
||||||
"fs",
|
|
||||||
"term",
|
|
||||||
] }
|
|
||||||
atty = "0.2"
|
atty = "0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.82.0" }
|
nu-test-support = { path="./crates/nu-test-support", version = "0.72.1" }
|
||||||
tempfile = "3.5"
|
tempfile = "3.2.0"
|
||||||
assert_cmd = "2.0"
|
assert_cmd = "2.0.2"
|
||||||
criterion = "0.5"
|
pretty_assertions = "1.0.0"
|
||||||
pretty_assertions = "1.0"
|
serial_test = "0.8.0"
|
||||||
serial_test = "2.0"
|
hamcrest2 = "0.3.0"
|
||||||
rstest = { version = "0.17", default-features = false }
|
rstest = {version = "0.15.0", default-features = false}
|
||||||
|
itertools = "0.10.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
plugin = [
|
plugin = ["nu-plugin", "nu-cli/plugin", "nu-parser/plugin", "nu-command/plugin", "nu-protocol/plugin", "nu-engine/plugin"]
|
||||||
"nu-plugin",
|
# extra used to be more useful but now it's the same as default. Leaving it in for backcompat with existing build scripts
|
||||||
"nu-cli/plugin",
|
extra = ["default"]
|
||||||
"nu-parser/plugin",
|
|
||||||
"nu-command/plugin",
|
|
||||||
"nu-protocol/plugin",
|
|
||||||
"nu-engine/plugin",
|
|
||||||
]
|
|
||||||
default = ["plugin", "which-support", "trash-support", "sqlite"]
|
default = ["plugin", "which-support", "trash-support", "sqlite"]
|
||||||
stable = ["default"]
|
stable = ["default"]
|
||||||
wasi = ["nu-cmd-lang/wasi"]
|
wasi = []
|
||||||
# NOTE: individual features are also passed to `nu-cmd-lang` that uses them to generate the feature matrix in the `version` command
|
|
||||||
|
|
||||||
# Enable to statically link OpenSSL; otherwise the system version will be used. Not enabled by default because it takes a while to build
|
# Enable to statically link OpenSSL; otherwise the system version will be used. Not enabled by default because it takes a while to build
|
||||||
static-link-openssl = ["dep:openssl", "nu-cmd-lang/static-link-openssl"]
|
static-link-openssl = ["dep:openssl"]
|
||||||
|
|
||||||
mimalloc = ["nu-cmd-lang/mimalloc", "dep:mimalloc"]
|
|
||||||
|
|
||||||
# Stable (Default)
|
# Stable (Default)
|
||||||
which-support = ["nu-command/which-support", "nu-cmd-lang/which-support"]
|
which-support = ["nu-command/which-support"]
|
||||||
trash-support = ["nu-command/trash-support", "nu-cmd-lang/trash-support"]
|
trash-support = ["nu-command/trash-support"]
|
||||||
|
|
||||||
# Extra feature for nushell
|
# Extra
|
||||||
extra = ["dep:nu-cmd-extra", "nu-cmd-lang/extra"]
|
|
||||||
|
|
||||||
# Dataframe feature for nushell
|
# Dataframe feature for nushell
|
||||||
dataframe = ["dep:nu-cmd-dataframe", "nu-cmd-lang/dataframe"]
|
dataframe = ["nu-command/dataframe"]
|
||||||
|
|
||||||
# SQLite commands for nushell
|
# SQLite commands for nushell
|
||||||
sqlite = ["nu-command/sqlite", "nu-cmd-lang/sqlite"]
|
sqlite = ["nu-command/sqlite"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = "s" # Optimize for size
|
opt-level = "s" # Optimize for size
|
||||||
strip = "debuginfo"
|
strip = "debuginfo"
|
||||||
lto = "thin"
|
lto = "thin"
|
||||||
|
|
||||||
@@ -160,17 +135,8 @@ debug = false
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "nu"
|
name = "nu"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
bench = false
|
|
||||||
|
|
||||||
# To use a development version of a dependency please use a global override here
|
# 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
|
# changing versions in each sub-crate of the workspace is tedious
|
||||||
[patch.crates-io]
|
[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"}
|
|
||||||
|
|
||||||
# Criterion benchmarking setup
|
|
||||||
# Run all benchmarks with `cargo bench`
|
|
||||||
# Run individual benchmarks like `cargo bench -- <regex>` e.g. `cargo bench -- parse`
|
|
||||||
[[bench]]
|
|
||||||
name = "benchmarks"
|
|
||||||
harness = false
|
|
||||||
|
@@ -1,9 +0,0 @@
|
|||||||
# Configuration for cross-rs: https://github.com/cross-rs/cross
|
|
||||||
# Run cross-rs like this:
|
|
||||||
# cross build --target aarch64-unknown-linux-musl --release
|
|
||||||
|
|
||||||
[target.aarch64-unknown-linux-gnu]
|
|
||||||
dockerfile = "./docker/cross-rs/aarch64-unknown-linux-gnu.dockerfile"
|
|
||||||
|
|
||||||
[target.aarch64-unknown-linux-musl]
|
|
||||||
dockerfile = "./docker/cross-rs/aarch64-unknown-linux-musl.dockerfile"
|
|
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 - 2023 The Nushell Project Developers
|
Copyright (c) 2019 - 2022 The Nushell Project Developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
58
README.md
58
README.md
@@ -1,29 +1,27 @@
|
|||||||
# Nushell <!-- omit in toc -->
|
# Nushell <!-- omit in toc -->
|
||||||
[](https://crates.io/crates/nu)
|
[](https://crates.io/crates/nu)
|
||||||
[](https://github.com/nushell/nushell/actions)
|

|
||||||
[](https://github.com/nushell/nushell/actions/workflows/nightly-build.yml)
|
|
||||||
[](https://discord.gg/NtAbbGn)
|
[](https://discord.gg/NtAbbGn)
|
||||||
[](https://changelog.com/podcast/363)
|
[](https://changelog.com/podcast/363)
|
||||||
[](https://twitter.com/nu_shell)
|
[](https://twitter.com/nu_shell)
|
||||||
[](https://github.com/nushell/nushell/graphs/commit-activity)
|

|
||||||
[](https://github.com/nushell/nushell/graphs/contributors)
|

|
||||||
[](https://codecov.io/gh/nushell/nushell)
|
|
||||||
|
|
||||||
A new type of shell.
|
A new type of shell.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Table of Contents <!-- omit in toc -->
|
## Table of Contents <!-- omit in toc -->
|
||||||
|
|
||||||
- [Status](#status)
|
- [Status](#status)
|
||||||
- [Learning About Nu](#learning-about-nu)
|
- [Learning About Nu](#learning-about-nu)
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
- [Configuration](#configuration)
|
|
||||||
- [Philosophy](#philosophy)
|
- [Philosophy](#philosophy)
|
||||||
- [Pipelines](#pipelines)
|
- [Pipelines](#pipelines)
|
||||||
- [Opening files](#opening-files)
|
- [Opening files](#opening-files)
|
||||||
- [Plugins](#plugins)
|
- [Plugins](#plugins)
|
||||||
- [Goals](#goals)
|
- [Goals](#goals)
|
||||||
|
- [Progress](#progress)
|
||||||
- [Officially Supported By](#officially-supported-by)
|
- [Officially Supported By](#officially-supported-by)
|
||||||
- [Contributing](#contributing)
|
- [Contributing](#contributing)
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
@@ -34,7 +32,7 @@ This project has reached a minimum-viable-product level of quality. Many people
|
|||||||
|
|
||||||
## Learning About Nu
|
## Learning About Nu
|
||||||
|
|
||||||
The [Nushell book](https://www.nushell.sh/book/) is the primary source of Nushell documentation. You can find [a full list of Nu commands in the book](https://www.nushell.sh/commands/), and we have many examples of using Nu in our [cookbook](https://www.nushell.sh/cookbook/).
|
The [Nushell book](https://www.nushell.sh/book/) is the primary source of Nushell documentation. You can find [a full list of Nu commands in the book](https://www.nushell.sh/book/command_reference.html), and we have many examples of using Nu in our [cookbook](https://www.nushell.sh/cookbook/).
|
||||||
|
|
||||||
We're also active on [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell); come and chat with us!
|
We're also active on [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell); come and chat with us!
|
||||||
|
|
||||||
@@ -49,29 +47,13 @@ brew install nushell
|
|||||||
winget install nushell
|
winget install nushell
|
||||||
```
|
```
|
||||||
|
|
||||||
To use `Nu` in GitHub Action, check [setup-nu](https://github.com/marketplace/actions/setup-nu) for more detail.
|
To use `Nu` in Github Action, check [setup-nu](https://github.com/marketplace/actions/setup-nu) for more detail.
|
||||||
|
|
||||||
Detailed installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). Nu is available via many package managers:
|
Detailed installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). Nu is available via many package managers:
|
||||||
|
|
||||||
[](https://repology.org/project/nushell/versions)
|
[](https://repology.org/project/nushell/versions)
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
The default configurations can be found at [sample_config](crates/nu-utils/src/sample_config)
|
|
||||||
which are the configuration files one gets when they startup Nushell for the first time.
|
|
||||||
|
|
||||||
It sets all of the default configuration to run Nushell. From here one can
|
|
||||||
then customize this file for their specific needs.
|
|
||||||
|
|
||||||
To see where *config.nu* is located on your system simply type this command.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
$nu.config-path
|
|
||||||
```
|
|
||||||
|
|
||||||
Please see our [book](https://www.nushell.sh) for all of the Nushell documentation.
|
|
||||||
|
|
||||||
|
|
||||||
## Philosophy
|
## Philosophy
|
||||||
|
|
||||||
@@ -192,9 +174,6 @@ These binaries interact with nu via a simple JSON-RPC protocol where the command
|
|||||||
If the plugin is a filter, data streams to it one element at a time, and it can stream data back in return via stdin/stdout.
|
If the plugin is a filter, data streams to it one element at a time, and it can stream data back in return via stdin/stdout.
|
||||||
If the plugin is a sink, it is given the full vector of final data and is given free reign over stdin/stdout to use as it pleases.
|
If the plugin is a sink, it is given the full vector of final data and is given free reign over stdin/stdout to use as it pleases.
|
||||||
|
|
||||||
The [awesome-nu repo](https://github.com/nushell/awesome-nu#plugins) lists a variety of nu-plugins while the [showcase repo](https://github.com/nushell/showcase) *shows* off informative blog posts that have been written about Nushell along with videos that highlight technical
|
|
||||||
topics that have been presented.
|
|
||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|
||||||
Nu adheres closely to a set of goals that make up its design philosophy. As features are added, they are checked against these goals.
|
Nu adheres closely to a set of goals that make up its design philosophy. As features are added, they are checked against these goals.
|
||||||
@@ -209,6 +188,27 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat
|
|||||||
|
|
||||||
- Finally, Nu views data functionally. Rather than using mutation, pipelines act as a means to load, change, and save data without mutable state.
|
- Finally, Nu views data functionally. Rather than using mutation, pipelines act as a means to load, change, and save data without mutable state.
|
||||||
|
|
||||||
|
## Progress
|
||||||
|
|
||||||
|
Nu is under heavy development and will naturally change as it matures. The chart below isn't meant to be exhaustive, but it helps give an idea for some of the areas of development and their relative maturity:
|
||||||
|
|
||||||
|
| Features | Not started | Prototype | MVP | Preview | Mature | Notes |
|
||||||
|
| ------------- | :---------: | :-------: | :-: | :-----: | :----: | -------------------------------------------------------------------- |
|
||||||
|
| Aliases | | | | X | | Aliases allow for shortening large commands, while passing flags |
|
||||||
|
| Notebook | | X | | | | Initial jupyter support, but it loses state and lacks features |
|
||||||
|
| File ops | | | | X | | cp, mv, rm, mkdir have some support, but lacking others |
|
||||||
|
| Environment | | | | X | | Temporary environment and scoped environment variables |
|
||||||
|
| Shells | | | | X | | Basic value and file shells, but no opt-in/opt-out for commands |
|
||||||
|
| Protocol | | | | X | | Streaming protocol is serviceable |
|
||||||
|
| Plugins | | | X | | | Plugins work on one row at a time, lack batching and expression eval |
|
||||||
|
| Errors | | | | X | | Error reporting works, but could use usability polish |
|
||||||
|
| Documentation | | | X | | | Book updated to latest release, including usage examples |
|
||||||
|
| Paging | | | | X | | Textview has paging, but we'd like paging for tables |
|
||||||
|
| Functions | | | | X | | Functions and aliases are supported |
|
||||||
|
| Variables | | | | X | | Nu supports variables and environment variables |
|
||||||
|
| Completions | | | | X | | Completions for filepaths |
|
||||||
|
| Type-checking | | | | x | | Commands check basic types, and input/output types |
|
||||||
|
|
||||||
## Officially Supported By
|
## Officially Supported By
|
||||||
|
|
||||||
Please submit an issue or PR to be added to this list.
|
Please submit an issue or PR to be added to this list.
|
||||||
@@ -218,8 +218,6 @@ Please submit an issue or PR to be added to this list.
|
|||||||
- [oh-my-posh](https://ohmyposh.dev)
|
- [oh-my-posh](https://ohmyposh.dev)
|
||||||
- [Couchbase Shell](https://couchbase.sh)
|
- [Couchbase Shell](https://couchbase.sh)
|
||||||
- [virtualenv](https://github.com/pypa/virtualenv)
|
- [virtualenv](https://github.com/pypa/virtualenv)
|
||||||
- [atuin](https://github.com/ellie/atuin)
|
|
||||||
- [clap](https://github.com/clap-rs/clap/tree/master/clap_complete_nushell)
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
3
README.release.txt
Normal file
3
README.release.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
To use Nu plugins, use the register command to tell Nu where to find the plugin. For example:
|
||||||
|
|
||||||
|
> register ./nu_plugin_query
|
@@ -1,7 +0,0 @@
|
|||||||
# Criterion benchmarks
|
|
||||||
|
|
||||||
These are benchmarks using [Criterion](https://github.com/bheisler/criterion.rs), a microbenchmarking tool for Rust.
|
|
||||||
|
|
||||||
Run all benchmarks with `cargo bench`
|
|
||||||
|
|
||||||
Or run individual benchmarks like `cargo bench -- <regex>` e.g. `cargo bench -- parse`
|
|
@@ -1,174 +0,0 @@
|
|||||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
|
||||||
use nu_cli::eval_source;
|
|
||||||
use nu_parser::parse;
|
|
||||||
use nu_plugin::{EncodingType, PluginResponse};
|
|
||||||
use nu_protocol::{engine::EngineState, PipelineData, Span, Value};
|
|
||||||
use nu_utils::{get_default_config, get_default_env};
|
|
||||||
|
|
||||||
fn load_bench_commands() -> EngineState {
|
|
||||||
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context())
|
|
||||||
}
|
|
||||||
// FIXME: All benchmarks live in this 1 file to speed up build times when benchmarking.
|
|
||||||
// When the *_benchmarks functions were in different files, `cargo bench` would build
|
|
||||||
// an executable for every single one - incredibly slowly. Would be nice to figure out
|
|
||||||
// a way to split things up again.
|
|
||||||
|
|
||||||
fn parser_benchmarks(c: &mut Criterion) {
|
|
||||||
let mut engine_state = load_bench_commands();
|
|
||||||
// parsing config.nu breaks without PWD set
|
|
||||||
engine_state.add_env_var(
|
|
||||||
"PWD".into(),
|
|
||||||
Value::string("/some/dir".to_string(), Span::test_data()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let default_env = get_default_env().as_bytes();
|
|
||||||
c.bench_function("parse_default_env_file", |b| {
|
|
||||||
b.iter_batched(
|
|
||||||
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|
|
||||||
|mut working_set| parse(&mut working_set, None, default_env, false),
|
|
||||||
BatchSize::SmallInput,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let default_config = get_default_config().as_bytes();
|
|
||||||
c.bench_function("parse_default_config_file", |b| {
|
|
||||||
b.iter_batched(
|
|
||||||
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|
|
||||||
|mut working_set| parse(&mut working_set, None, default_config, false),
|
|
||||||
BatchSize::SmallInput,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
c.bench_function("eval default_env.nu", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut engine_state = load_bench_commands();
|
|
||||||
let mut stack = nu_protocol::engine::Stack::new();
|
|
||||||
eval_source(
|
|
||||||
&mut engine_state,
|
|
||||||
&mut stack,
|
|
||||||
get_default_env().as_bytes(),
|
|
||||||
"default_env.nu",
|
|
||||||
PipelineData::empty(),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
c.bench_function("eval default_config.nu", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut engine_state = load_bench_commands();
|
|
||||||
// parsing config.nu breaks without PWD set
|
|
||||||
engine_state.add_env_var(
|
|
||||||
"PWD".into(),
|
|
||||||
Value::string("/some/dir".to_string(), Span::test_data()),
|
|
||||||
);
|
|
||||||
let mut stack = nu_protocol::engine::Stack::new();
|
|
||||||
eval_source(
|
|
||||||
&mut engine_state,
|
|
||||||
&mut stack,
|
|
||||||
get_default_config().as_bytes(),
|
|
||||||
"default_config.nu",
|
|
||||||
PipelineData::empty(),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eval_benchmarks(c: &mut Criterion) {
|
|
||||||
c.bench_function("eval default_env.nu", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut engine_state = load_bench_commands();
|
|
||||||
let mut stack = nu_protocol::engine::Stack::new();
|
|
||||||
eval_source(
|
|
||||||
&mut engine_state,
|
|
||||||
&mut stack,
|
|
||||||
get_default_env().as_bytes(),
|
|
||||||
"default_env.nu",
|
|
||||||
PipelineData::empty(),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
c.bench_function("eval default_config.nu", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut engine_state = load_bench_commands();
|
|
||||||
// parsing config.nu breaks without PWD set
|
|
||||||
engine_state.add_env_var(
|
|
||||||
"PWD".into(),
|
|
||||||
Value::string("/some/dir".to_string(), Span::test_data()),
|
|
||||||
);
|
|
||||||
let mut stack = nu_protocol::engine::Stack::new();
|
|
||||||
eval_source(
|
|
||||||
&mut engine_state,
|
|
||||||
&mut stack,
|
|
||||||
get_default_config().as_bytes(),
|
|
||||||
"default_config.nu",
|
|
||||||
PipelineData::empty(),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate a new table data with `row_cnt` rows, `col_cnt` columns.
|
|
||||||
fn encoding_test_data(row_cnt: usize, col_cnt: usize) -> Value {
|
|
||||||
let columns: Vec<String> = (0..col_cnt).map(|x| format!("col_{x}")).collect();
|
|
||||||
let vals: Vec<Value> = (0..col_cnt as i64).map(Value::test_int).collect();
|
|
||||||
|
|
||||||
Value::List {
|
|
||||||
vals: (0..row_cnt)
|
|
||||||
.map(|_| Value::test_record(columns.clone(), vals.clone()))
|
|
||||||
.collect(),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encoding_benchmarks(c: &mut Criterion) {
|
|
||||||
let mut group = c.benchmark_group("Encoding");
|
|
||||||
let test_cnt_pairs = [(100, 5), (100, 15), (10000, 5), (10000, 15)];
|
|
||||||
for (row_cnt, col_cnt) in test_cnt_pairs.into_iter() {
|
|
||||||
for fmt in ["json", "msgpack"] {
|
|
||||||
group.bench_function(&format!("{fmt} encode {row_cnt} * {col_cnt}"), |b| {
|
|
||||||
let mut res = vec![];
|
|
||||||
let test_data =
|
|
||||||
PluginResponse::Value(Box::new(encoding_test_data(row_cnt, col_cnt)));
|
|
||||||
let encoder = EncodingType::try_from_bytes(fmt.as_bytes()).unwrap();
|
|
||||||
b.iter(|| encoder.encode_response(&test_data, &mut res))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decoding_benchmarks(c: &mut Criterion) {
|
|
||||||
let mut group = c.benchmark_group("Decoding");
|
|
||||||
let test_cnt_pairs = [(100, 5), (100, 15), (10000, 5), (10000, 15)];
|
|
||||||
for (row_cnt, col_cnt) in test_cnt_pairs.into_iter() {
|
|
||||||
for fmt in ["json", "msgpack"] {
|
|
||||||
group.bench_function(&format!("{fmt} decode for {row_cnt} * {col_cnt}"), |b| {
|
|
||||||
let mut res = vec![];
|
|
||||||
let test_data =
|
|
||||||
PluginResponse::Value(Box::new(encoding_test_data(row_cnt, col_cnt)));
|
|
||||||
let encoder = EncodingType::try_from_bytes(fmt.as_bytes()).unwrap();
|
|
||||||
encoder.encode_response(&test_data, &mut res).unwrap();
|
|
||||||
let mut binary_data = std::io::Cursor::new(res);
|
|
||||||
b.iter(|| {
|
|
||||||
binary_data.set_position(0);
|
|
||||||
encoder.decode_response(&mut binary_data)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(
|
|
||||||
benches,
|
|
||||||
parser_benchmarks,
|
|
||||||
eval_benchmarks,
|
|
||||||
encoding_benchmarks,
|
|
||||||
decoding_benchmarks
|
|
||||||
);
|
|
||||||
criterion_main!(benches);
|
|
@@ -1,10 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
DIR=$(readlink -f $(dirname "${BASH_SOURCE[0]}"))
|
|
||||||
REPO_ROOT=$(dirname $DIR)
|
|
||||||
|
|
||||||
echo "---------------------------------------------------------------"
|
echo "---------------------------------------------------------------"
|
||||||
echo "Building nushell (nu) with dataframes and all the plugins"
|
echo "Building nushell (nu) with dataframes and all the plugins"
|
||||||
echo "---------------------------------------------------------------"
|
echo "---------------------------------------------------------------"
|
||||||
@@ -19,17 +15,11 @@ NU_PLUGINS=(
|
|||||||
)
|
)
|
||||||
|
|
||||||
echo "Building nushell"
|
echo "Building nushell"
|
||||||
(
|
cargo build --features=dataframe
|
||||||
cd $REPO_ROOT
|
|
||||||
cargo build --features=dataframe
|
|
||||||
)
|
|
||||||
|
|
||||||
for plugin in "${NU_PLUGINS[@]}"
|
for plugin in "${NU_PLUGINS[@]}"
|
||||||
do
|
do
|
||||||
|
echo '' && cd crates/"$plugin"
|
||||||
echo "Building $plugin..."
|
echo "Building $plugin..."
|
||||||
echo "-----------------------------"
|
echo "-----------------------------"
|
||||||
(
|
cargo build && cd ../..
|
||||||
cd "$REPO_ROOT/crates/$plugin"
|
|
||||||
cargo build
|
|
||||||
)
|
|
||||||
done
|
done
|
36
build-all-windows.cmd
Normal file
36
build-all-windows.cmd
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
@echo off
|
||||||
|
@echo -------------------------------------------------------------------
|
||||||
|
@echo Building nushell (nu.exe) with dataframes and all the plugins
|
||||||
|
@echo -------------------------------------------------------------------
|
||||||
|
@echo.
|
||||||
|
|
||||||
|
echo Building nushell.exe
|
||||||
|
cargo build cargo build --features=dataframe
|
||||||
|
@echo.
|
||||||
|
|
||||||
|
@cd crates\nu_plugin_example
|
||||||
|
echo Building nu_plugin_example.exe
|
||||||
|
cargo build
|
||||||
|
@echo.
|
||||||
|
|
||||||
|
@cd ..\..\crates\nu_plugin_gstat
|
||||||
|
echo Building nu_plugin_gstat.exe
|
||||||
|
cargo build
|
||||||
|
@echo.
|
||||||
|
|
||||||
|
@cd ..\..\crates\nu_plugin_inc
|
||||||
|
echo Building nu_plugin_inc.exe
|
||||||
|
cargo build
|
||||||
|
@echo.
|
||||||
|
|
||||||
|
@cd ..\..\crates\nu_plugin_query
|
||||||
|
echo Building nu_plugin_query.exe
|
||||||
|
cargo build
|
||||||
|
@echo.
|
||||||
|
|
||||||
|
@cd ..\..\crates\nu_plugin_custom_values
|
||||||
|
echo Building nu_plugin_custom_values.exe
|
||||||
|
cargo build
|
||||||
|
@echo.
|
||||||
|
|
||||||
|
@cd ..\..
|
23
build-all.nu
Normal file
23
build-all.nu
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
echo '-------------------------------------------------------------------'
|
||||||
|
echo 'Building nushell (nu) with dataframes and all the plugins'
|
||||||
|
echo '-------------------------------------------------------------------'
|
||||||
|
|
||||||
|
echo $'(char nl)Building nushell'
|
||||||
|
echo '----------------------------'
|
||||||
|
cargo build --features=dataframe
|
||||||
|
|
||||||
|
let plugins = [
|
||||||
|
nu_plugin_inc,
|
||||||
|
nu_plugin_gstat,
|
||||||
|
nu_plugin_query,
|
||||||
|
nu_plugin_example,
|
||||||
|
nu_plugin_custom_values,
|
||||||
|
]
|
||||||
|
|
||||||
|
for plugin in $plugins {
|
||||||
|
$'(char nl)Building ($plugin)'
|
||||||
|
'----------------------------'
|
||||||
|
cd $'crates/($plugin)'
|
||||||
|
cargo build
|
||||||
|
ignore
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut res = winresource::WindowsResource::new();
|
let mut res = winres::WindowsResource::new();
|
||||||
res.set("ProductName", "Nushell");
|
res.set("ProductName", "Nushell");
|
||||||
res.set("FileDescription", "Nushell");
|
res.set("FileDescription", "Nushell");
|
||||||
res.set("LegalCopyright", "Copyright (C) 2022");
|
res.set("LegalCopyright", "Copyright (C) 2022");
|
17
codecov.yml
17
codecov.yml
@@ -1,17 +0,0 @@
|
|||||||
coverage:
|
|
||||||
status:
|
|
||||||
project:
|
|
||||||
default:
|
|
||||||
target: 55%
|
|
||||||
threshold: 2%
|
|
||||||
patch:
|
|
||||||
default:
|
|
||||||
informational: true
|
|
||||||
|
|
||||||
comment:
|
|
||||||
layout: reach, diff, files
|
|
||||||
behavior: default
|
|
||||||
require_base: yes
|
|
||||||
require_head: yes
|
|
||||||
after_n_builds: 1 # Disabled windows else: 2
|
|
||||||
|
|
@@ -5,40 +5,35 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cli"
|
name = "nu-cli"
|
||||||
version = "0.82.0"
|
version = "0.72.1"
|
||||||
|
|
||||||
[lib]
|
|
||||||
bench = false
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.82.0" }
|
nu-test-support = { path="../nu-test-support", version = "0.72.1" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.82.0" }
|
nu-command = { path = "../nu-command", version = "0.72.1" }
|
||||||
rstest = { version = "0.17.0", default-features = false }
|
rstest = {version = "0.15.0", default-features = false}
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.82.0" }
|
nu-engine = { path = "../nu-engine", version = "0.72.1" }
|
||||||
nu-command = { path = "../nu-command", version = "0.82.0" }
|
nu-path = { path = "../nu-path", version = "0.72.1" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.82.0" }
|
nu-parser = { path = "../nu-parser", version = "0.72.1" }
|
||||||
nu-path = { path = "../nu-path", version = "0.82.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.72.1" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.82.0" }
|
nu-utils = { path = "../nu-utils", version = "0.72.1" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.82.0" }
|
nu-ansi-term = "0.46.0"
|
||||||
nu-utils = { path = "../nu-utils", version = "0.82.0" }
|
nu-color-config = { path = "../nu-color-config", version = "0.72.1" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.82.0" }
|
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
||||||
nu-ansi-term = "0.47.0"
|
|
||||||
reedline = { version = "0.21.0", features = ["bashisms", "sqlite"]}
|
|
||||||
|
|
||||||
atty = "0.2"
|
atty = "0.2.14"
|
||||||
chrono = { default-features = false, features = ["std"], version = "0.4" }
|
chrono = { default-features = false, features = ["std"], version = "0.4.23" }
|
||||||
crossterm = "0.26"
|
crossterm = "0.24.0"
|
||||||
fancy-regex = "0.11"
|
fancy-regex = "0.10.0"
|
||||||
fuzzy-matcher = "0.3"
|
fuzzy-matcher = "0.3.7"
|
||||||
is_executable = "1.0"
|
is_executable = "1.0.1"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
miette = { version = "5.9", features = ["fancy-no-backtrace"] }
|
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
|
||||||
once_cell = "1.18"
|
|
||||||
percent-encoding = "2"
|
percent-encoding = "2"
|
||||||
sysinfo = "0.29"
|
sysinfo = "0.26.2"
|
||||||
unicode-segmentation = "1.10"
|
thiserror = "1.0.31"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
plugin = []
|
plugin = []
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 - 2023 The Nushell Project Developers
|
Copyright (c) 2019 - 2022 The Nushell Project Developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
|
use crate::util::report_error;
|
||||||
use log::info;
|
use log::info;
|
||||||
use miette::Result;
|
use miette::Result;
|
||||||
use nu_engine::{convert_env_values, eval_block};
|
use nu_engine::{convert_env_values, eval_block};
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::engine::Stack;
|
use nu_protocol::engine::Stack;
|
||||||
use nu_protocol::report_error;
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, StateWorkingSet},
|
engine::{EngineState, StateWorkingSet},
|
||||||
PipelineData, Spanned, Value,
|
PipelineData, Spanned, Value,
|
||||||
@@ -34,9 +34,9 @@ pub fn evaluate_commands(
|
|||||||
|
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
let output = parse(&mut working_set, None, commands.item.as_bytes(), false);
|
let (output, err) = parse(&mut working_set, None, commands.item.as_bytes(), false, &[]);
|
||||||
if let Some(err) = working_set.parse_errors.first() {
|
if let Some(err) = err {
|
||||||
report_error(&working_set, err);
|
report_error(&working_set, &err);
|
||||||
|
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
@@ -1,126 +0,0 @@
|
|||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::Category;
|
|
||||||
use nu_protocol::IntoPipelineData;
|
|
||||||
use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape, Type, Value};
|
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Commandline;
|
|
||||||
|
|
||||||
impl Command for Commandline {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"commandline"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("commandline")
|
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
|
||||||
.switch(
|
|
||||||
"cursor",
|
|
||||||
"Set or get the current cursor position",
|
|
||||||
Some('c'),
|
|
||||||
)
|
|
||||||
.switch(
|
|
||||||
"append",
|
|
||||||
"appends the string to the end of the buffer",
|
|
||||||
Some('a'),
|
|
||||||
)
|
|
||||||
.switch(
|
|
||||||
"insert",
|
|
||||||
"inserts the string into the buffer at the cursor position",
|
|
||||||
Some('i'),
|
|
||||||
)
|
|
||||||
.switch(
|
|
||||||
"replace",
|
|
||||||
"replaces the current contents of the buffer (default)",
|
|
||||||
Some('r'),
|
|
||||||
)
|
|
||||||
.optional(
|
|
||||||
"cmd",
|
|
||||||
SyntaxShape::String,
|
|
||||||
"the string to perform the operation with",
|
|
||||||
)
|
|
||||||
.category(Category::Core)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"View or modify the current command line input buffer."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["repl", "interactive"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
if let Some(cmd) = call.opt::<Value>(engine_state, stack, 0)? {
|
|
||||||
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
|
|
||||||
|
|
||||||
if call.has_flag("cursor") {
|
|
||||||
let cmd_str = cmd.as_string()?;
|
|
||||||
match cmd_str.parse::<i64>() {
|
|
||||||
Ok(n) => {
|
|
||||||
repl.cursor_pos = if n <= 0 {
|
|
||||||
0usize
|
|
||||||
} else {
|
|
||||||
repl.buffer
|
|
||||||
.grapheme_indices(true)
|
|
||||||
.map(|(i, _c)| i)
|
|
||||||
.nth(n as usize)
|
|
||||||
.unwrap_or(repl.buffer.len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
return Err(ShellError::CantConvert {
|
|
||||||
to_type: "int".to_string(),
|
|
||||||
from_type: "string".to_string(),
|
|
||||||
span: cmd.span()?,
|
|
||||||
help: Some(format!(
|
|
||||||
r#"string "{cmd_str}" does not represent a valid integer"#
|
|
||||||
)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if call.has_flag("append") {
|
|
||||||
repl.buffer.push_str(&cmd.as_string()?);
|
|
||||||
} else if call.has_flag("insert") {
|
|
||||||
let cmd_str = cmd.as_string()?;
|
|
||||||
let cursor_pos = repl.cursor_pos;
|
|
||||||
repl.buffer.insert_str(cursor_pos, &cmd_str);
|
|
||||||
repl.cursor_pos += cmd_str.len();
|
|
||||||
} else {
|
|
||||||
repl.buffer = cmd.as_string()?;
|
|
||||||
repl.cursor_pos = repl.buffer.len();
|
|
||||||
}
|
|
||||||
Ok(Value::Nothing { span: call.head }.into_pipeline_data())
|
|
||||||
} else {
|
|
||||||
let repl = engine_state.repl_state.lock().expect("repl state mutex");
|
|
||||||
if call.has_flag("cursor") {
|
|
||||||
let char_pos = repl
|
|
||||||
.buffer
|
|
||||||
.grapheme_indices(true)
|
|
||||||
.chain(std::iter::once((repl.buffer.len(), "")))
|
|
||||||
.position(|(i, _c)| i == repl.cursor_pos)
|
|
||||||
.expect("Cursor position isn't on a grapheme boundary");
|
|
||||||
Ok(Value::String {
|
|
||||||
val: char_pos.to_string(),
|
|
||||||
span: call.head,
|
|
||||||
}
|
|
||||||
.into_pipeline_data())
|
|
||||||
} else {
|
|
||||||
Ok(Value::String {
|
|
||||||
val: repl.buffer.to_string(),
|
|
||||||
span: call.head,
|
|
||||||
}
|
|
||||||
.into_pipeline_data())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,33 +0,0 @@
|
|||||||
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
|
||||||
|
|
||||||
use crate::commands::*;
|
|
||||||
|
|
||||||
pub fn add_cli_context(mut engine_state: EngineState) -> EngineState {
|
|
||||||
let delta = {
|
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
|
||||||
|
|
||||||
macro_rules! bind_command {
|
|
||||||
( $( $command:expr ),* $(,)? ) => {
|
|
||||||
$( working_set.add_decl(Box::new($command)); )*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
bind_command! {
|
|
||||||
Commandline,
|
|
||||||
History,
|
|
||||||
HistorySession,
|
|
||||||
Keybindings,
|
|
||||||
KeybindingsDefault,
|
|
||||||
KeybindingsList,
|
|
||||||
KeybindingsListen,
|
|
||||||
};
|
|
||||||
|
|
||||||
working_set.render()
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(err) = engine_state.merge_delta(delta) {
|
|
||||||
eprintln!("Error creating CLI command context: {err:?}");
|
|
||||||
}
|
|
||||||
|
|
||||||
engine_state
|
|
||||||
}
|
|
@@ -1,264 +0,0 @@
|
|||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, HistoryFileFormat, IntoInterruptiblePipelineData, PipelineData, ShellError,
|
|
||||||
Signature, Span, Type, Value,
|
|
||||||
};
|
|
||||||
use reedline::{
|
|
||||||
FileBackedHistory, History as ReedlineHistory, HistoryItem, SearchDirection, SearchQuery,
|
|
||||||
SqliteBackedHistory,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct History;
|
|
||||||
|
|
||||||
impl Command for History {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"history"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Get the command history."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
|
||||||
Signature::build("history")
|
|
||||||
.input_output_types(vec![
|
|
||||||
(Type::Nothing, Type::Table(vec![])),
|
|
||||||
(Type::Nothing, Type::Nothing),
|
|
||||||
])
|
|
||||||
.allow_variants_without_examples(true)
|
|
||||||
.switch("clear", "Clears out the history entries", Some('c'))
|
|
||||||
.switch(
|
|
||||||
"long",
|
|
||||||
"Show long listing of entries for sqlite history",
|
|
||||||
Some('l'),
|
|
||||||
)
|
|
||||||
.category(Category::Misc)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
_stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
|
|
||||||
// todo for sqlite history this command should be an alias to `open ~/.config/nushell/history.sqlite3 | get history`
|
|
||||||
if let Some(config_path) = nu_path::config_dir() {
|
|
||||||
let clear = call.has_flag("clear");
|
|
||||||
let long = call.has_flag("long");
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
|
||||||
|
|
||||||
let mut history_path = config_path;
|
|
||||||
history_path.push("nushell");
|
|
||||||
match engine_state.config.history_file_format {
|
|
||||||
HistoryFileFormat::Sqlite => {
|
|
||||||
history_path.push("history.sqlite3");
|
|
||||||
}
|
|
||||||
HistoryFileFormat::PlainText => {
|
|
||||||
history_path.push("history.txt");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if clear {
|
|
||||||
let _ = std::fs::remove_file(history_path);
|
|
||||||
// TODO: FIXME also clear the auxiliary files when using sqlite
|
|
||||||
Ok(PipelineData::empty())
|
|
||||||
} else {
|
|
||||||
let history_reader: Option<Box<dyn ReedlineHistory>> =
|
|
||||||
match engine_state.config.history_file_format {
|
|
||||||
HistoryFileFormat::Sqlite => SqliteBackedHistory::with_file(history_path)
|
|
||||||
.map(|inner| {
|
|
||||||
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
|
|
||||||
boxed
|
|
||||||
})
|
|
||||||
.ok(),
|
|
||||||
|
|
||||||
HistoryFileFormat::PlainText => FileBackedHistory::with_file(
|
|
||||||
engine_state.config.max_history_size as usize,
|
|
||||||
history_path,
|
|
||||||
)
|
|
||||||
.map(|inner| {
|
|
||||||
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
|
|
||||||
boxed
|
|
||||||
})
|
|
||||||
.ok(),
|
|
||||||
};
|
|
||||||
|
|
||||||
match engine_state.config.history_file_format {
|
|
||||||
HistoryFileFormat::PlainText => Ok(history_reader
|
|
||||||
.and_then(|h| {
|
|
||||||
h.search(SearchQuery::everything(SearchDirection::Forward, None))
|
|
||||||
.ok()
|
|
||||||
})
|
|
||||||
.map(move |entries| {
|
|
||||||
entries
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(move |(idx, entry)| Value::Record {
|
|
||||||
cols: vec!["command".to_string(), "index".to_string()],
|
|
||||||
vals: vec![
|
|
||||||
Value::String {
|
|
||||||
val: entry.command_line,
|
|
||||||
span: head,
|
|
||||||
},
|
|
||||||
Value::int(idx as i64, head),
|
|
||||||
],
|
|
||||||
span: head,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.ok_or(ShellError::FileNotFound(head))?
|
|
||||||
.into_pipeline_data(ctrlc)),
|
|
||||||
HistoryFileFormat::Sqlite => Ok(history_reader
|
|
||||||
.and_then(|h| {
|
|
||||||
h.search(SearchQuery::everything(SearchDirection::Forward, None))
|
|
||||||
.ok()
|
|
||||||
})
|
|
||||||
.map(move |entries| {
|
|
||||||
entries.into_iter().enumerate().map(move |(idx, entry)| {
|
|
||||||
create_history_record(idx, entry, long, head)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.ok_or(ShellError::FileNotFound(head))?
|
|
||||||
.into_pipeline_data(ctrlc)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(ShellError::FileNotFound(head))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
example: "history | length",
|
|
||||||
description: "Get current history length",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
example: "history | last 5",
|
|
||||||
description: "Show last 5 commands you have ran",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
example: "history | wrap cmd | where cmd =~ cargo",
|
|
||||||
description: "Search all the commands from history that contains 'cargo'",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_history_record(idx: usize, entry: HistoryItem, long: bool, head: Span) -> Value {
|
|
||||||
//1. Format all the values
|
|
||||||
//2. Create a record of either short or long columns and values
|
|
||||||
|
|
||||||
let item_id_value = Value::Int {
|
|
||||||
val: match entry.id {
|
|
||||||
Some(id) => {
|
|
||||||
let ids = id.to_string();
|
|
||||||
match ids.parse::<i64>() {
|
|
||||||
Ok(i) => i,
|
|
||||||
_ => 0i64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => 0i64,
|
|
||||||
},
|
|
||||||
span: head,
|
|
||||||
};
|
|
||||||
let start_timestamp_value = Value::String {
|
|
||||||
val: match entry.start_timestamp {
|
|
||||||
Some(time) => time.to_string(),
|
|
||||||
None => "".into(),
|
|
||||||
},
|
|
||||||
span: head,
|
|
||||||
};
|
|
||||||
let command_value = Value::String {
|
|
||||||
val: entry.command_line,
|
|
||||||
span: head,
|
|
||||||
};
|
|
||||||
let session_id_value = Value::Int {
|
|
||||||
val: match entry.session_id {
|
|
||||||
Some(sid) => {
|
|
||||||
let sids = sid.to_string();
|
|
||||||
match sids.parse::<i64>() {
|
|
||||||
Ok(i) => i,
|
|
||||||
_ => 0i64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => 0i64,
|
|
||||||
},
|
|
||||||
span: head,
|
|
||||||
};
|
|
||||||
let hostname_value = Value::String {
|
|
||||||
val: match entry.hostname {
|
|
||||||
Some(host) => host,
|
|
||||||
None => "".into(),
|
|
||||||
},
|
|
||||||
span: head,
|
|
||||||
};
|
|
||||||
let cwd_value = Value::String {
|
|
||||||
val: match entry.cwd {
|
|
||||||
Some(cwd) => cwd,
|
|
||||||
None => "".into(),
|
|
||||||
},
|
|
||||||
span: head,
|
|
||||||
};
|
|
||||||
let duration_value = Value::Duration {
|
|
||||||
val: match entry.duration {
|
|
||||||
Some(d) => d.as_nanos().try_into().unwrap_or(0),
|
|
||||||
None => 0,
|
|
||||||
},
|
|
||||||
span: head,
|
|
||||||
};
|
|
||||||
let exit_status_value = Value::int(entry.exit_status.unwrap_or(0), head);
|
|
||||||
let index_value = Value::int(idx as i64, head);
|
|
||||||
if long {
|
|
||||||
Value::Record {
|
|
||||||
cols: vec![
|
|
||||||
"item_id".into(),
|
|
||||||
"start_timestamp".into(),
|
|
||||||
"command".to_string(),
|
|
||||||
"session_id".into(),
|
|
||||||
"hostname".into(),
|
|
||||||
"cwd".into(),
|
|
||||||
"duration".into(),
|
|
||||||
"exit_status".into(),
|
|
||||||
"idx".to_string(),
|
|
||||||
],
|
|
||||||
vals: vec![
|
|
||||||
item_id_value,
|
|
||||||
start_timestamp_value,
|
|
||||||
command_value,
|
|
||||||
session_id_value,
|
|
||||||
hostname_value,
|
|
||||||
cwd_value,
|
|
||||||
duration_value,
|
|
||||||
exit_status_value,
|
|
||||||
index_value,
|
|
||||||
],
|
|
||||||
span: head,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Value::Record {
|
|
||||||
cols: vec![
|
|
||||||
"start_timestamp".into(),
|
|
||||||
"command".to_string(),
|
|
||||||
"cwd".into(),
|
|
||||||
"duration".into(),
|
|
||||||
"exit_status".into(),
|
|
||||||
],
|
|
||||||
vals: vec![
|
|
||||||
start_timestamp_value,
|
|
||||||
command_value,
|
|
||||||
cwd_value,
|
|
||||||
duration_value,
|
|
||||||
exit_status_value,
|
|
||||||
],
|
|
||||||
span: head,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -37,8 +37,7 @@ impl CommandCompletion {
|
|||||||
) -> Vec<String> {
|
) -> Vec<String> {
|
||||||
let mut executables = vec![];
|
let mut executables = vec![];
|
||||||
|
|
||||||
// os agnostic way to get the PATH env var
|
let paths = self.engine_state.get_env_var("PATH");
|
||||||
let paths = self.engine_state.get_path_env_var();
|
|
||||||
|
|
||||||
if let Some(paths) = paths {
|
if let Some(paths) = paths {
|
||||||
if let Ok(paths) = paths.as_list() {
|
if let Ok(paths) = paths.as_list() {
|
||||||
@@ -88,17 +87,35 @@ impl CommandCompletion {
|
|||||||
|
|
||||||
let filter_predicate = |command: &[u8]| match_algorithm.matches_u8(command, partial);
|
let filter_predicate = |command: &[u8]| match_algorithm.matches_u8(command, partial);
|
||||||
|
|
||||||
let mut results = working_set
|
let results = working_set
|
||||||
.find_commands_by_predicate(filter_predicate)
|
.find_commands_by_predicate(filter_predicate)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |x| Suggestion {
|
.map(move |x| Suggestion {
|
||||||
value: String::from_utf8_lossy(&x.0).to_string(),
|
value: String::from_utf8_lossy(&x.0).to_string(),
|
||||||
description: x.1,
|
description: x.1,
|
||||||
extra: None,
|
extra: None,
|
||||||
span: reedline::Span::new(span.start - offset, span.end - offset),
|
span: reedline::Span {
|
||||||
|
start: span.start - offset,
|
||||||
|
end: span.end - offset,
|
||||||
|
},
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
})
|
});
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
let results_aliases = working_set
|
||||||
|
.find_aliases_by_predicate(filter_predicate)
|
||||||
|
.into_iter()
|
||||||
|
.map(move |x| Suggestion {
|
||||||
|
value: String::from_utf8_lossy(&x).to_string(),
|
||||||
|
description: None,
|
||||||
|
extra: None,
|
||||||
|
span: reedline::Span {
|
||||||
|
start: span.start - offset,
|
||||||
|
end: span.end - offset,
|
||||||
|
},
|
||||||
|
append_whitespace: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut results = results.chain(results_aliases).collect::<Vec<_>>();
|
||||||
|
|
||||||
let partial = working_set.get_span_contents(span);
|
let partial = working_set.get_span_contents(span);
|
||||||
let partial = String::from_utf8_lossy(partial).to_string();
|
let partial = String::from_utf8_lossy(partial).to_string();
|
||||||
@@ -111,15 +128,15 @@ impl CommandCompletion {
|
|||||||
value: x,
|
value: x,
|
||||||
description: None,
|
description: None,
|
||||||
extra: None,
|
extra: None,
|
||||||
span: reedline::Span::new(span.start - offset, span.end - offset),
|
span: reedline::Span {
|
||||||
|
start: span.start - offset,
|
||||||
|
end: span.end - offset,
|
||||||
|
},
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
let results_strings: Vec<String> =
|
|
||||||
results.clone().into_iter().map(|x| x.value).collect();
|
|
||||||
|
|
||||||
for external in results_external {
|
for external in results_external {
|
||||||
if results_strings.contains(&external.value) {
|
if results.contains(&external) {
|
||||||
results.push(Suggestion {
|
results.push(Suggestion {
|
||||||
value: format!("^{}", external.value),
|
value: format!("^{}", external.value),
|
||||||
description: None,
|
description: None,
|
||||||
@@ -157,7 +174,7 @@ impl Completer for CommandCompletion {
|
|||||||
.take_while(|x| {
|
.take_while(|x| {
|
||||||
matches!(
|
matches!(
|
||||||
x.1,
|
x.1,
|
||||||
FlatShape::InternalCall(_)
|
FlatShape::InternalCall
|
||||||
| FlatShape::External
|
| FlatShape::External
|
||||||
| FlatShape::ExternalArg
|
| FlatShape::ExternalArg
|
||||||
| FlatShape::Literal
|
| FlatShape::Literal
|
||||||
@@ -170,7 +187,10 @@ impl Completer for CommandCompletion {
|
|||||||
let subcommands = if let Some(last) = last {
|
let subcommands = if let Some(last) = last {
|
||||||
self.complete_commands(
|
self.complete_commands(
|
||||||
working_set,
|
working_set,
|
||||||
Span::new(last.0.start, pos),
|
Span {
|
||||||
|
start: last.0.start,
|
||||||
|
end: pos,
|
||||||
|
},
|
||||||
offset,
|
offset,
|
||||||
false,
|
false,
|
||||||
options.match_algorithm,
|
options.match_algorithm,
|
||||||
@@ -185,9 +205,8 @@ impl Completer for CommandCompletion {
|
|||||||
|
|
||||||
let config = working_set.get_config();
|
let config = working_set.get_config();
|
||||||
let commands = if matches!(self.flat_shape, nu_parser::FlatShape::External)
|
let commands = if matches!(self.flat_shape, nu_parser::FlatShape::External)
|
||||||
|| matches!(self.flat_shape, nu_parser::FlatShape::InternalCall(_))
|
|| matches!(self.flat_shape, nu_parser::FlatShape::InternalCall)
|
||||||
|| ((span.end - span.start) == 0)
|
|| ((span.end - span.start) == 0)
|
||||||
|| is_passthrough_command(working_set.delta.get_file_contents())
|
|
||||||
{
|
{
|
||||||
// we're in a gap or at a command
|
// we're in a gap or at a command
|
||||||
if working_set.get_span_contents(span).is_empty() && !self.force_completion_after_space
|
if working_set.get_span_contents(span).is_empty() && !self.force_completion_after_space
|
||||||
@@ -215,107 +234,3 @@ impl Completer for CommandCompletion {
|
|||||||
SortBy::LevenshteinDistance
|
SortBy::LevenshteinDistance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_non_whitespace_index(contents: &[u8], start: usize) -> usize {
|
|
||||||
match contents.get(start..) {
|
|
||||||
Some(contents) => {
|
|
||||||
contents
|
|
||||||
.iter()
|
|
||||||
.take_while(|x| x.is_ascii_whitespace())
|
|
||||||
.count()
|
|
||||||
+ start
|
|
||||||
}
|
|
||||||
None => start,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_passthrough_command(working_set_file_contents: &[(Vec<u8>, usize, usize)]) -> bool {
|
|
||||||
for (contents, _, _) in working_set_file_contents {
|
|
||||||
let last_pipe_pos_rev = contents.iter().rev().position(|x| x == &b'|');
|
|
||||||
let last_pipe_pos = last_pipe_pos_rev.map(|x| contents.len() - x).unwrap_or(0);
|
|
||||||
|
|
||||||
let cur_pos = find_non_whitespace_index(contents, last_pipe_pos);
|
|
||||||
|
|
||||||
let result = match contents.get(cur_pos..) {
|
|
||||||
Some(contents) => contents.starts_with(b"sudo "),
|
|
||||||
None => false,
|
|
||||||
};
|
|
||||||
if result {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod command_completions_tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_find_non_whitespace_index() {
|
|
||||||
let commands = vec![
|
|
||||||
(" hello", 4),
|
|
||||||
("sudo ", 0),
|
|
||||||
(" sudo ", 2),
|
|
||||||
(" sudo ", 2),
|
|
||||||
(" hello ", 1),
|
|
||||||
(" hello ", 3),
|
|
||||||
(" hello | sudo ", 4),
|
|
||||||
(" sudo|sudo", 5),
|
|
||||||
("sudo | sudo ", 0),
|
|
||||||
(" hello sud", 1),
|
|
||||||
];
|
|
||||||
for (idx, ele) in commands.iter().enumerate() {
|
|
||||||
let index = find_non_whitespace_index(&Vec::from(ele.0.as_bytes()), 0);
|
|
||||||
assert_eq!(index, ele.1, "Failed on index {}", idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_is_last_command_passthrough() {
|
|
||||||
let commands = vec![
|
|
||||||
(" hello", false),
|
|
||||||
(" sudo ", true),
|
|
||||||
("sudo ", true),
|
|
||||||
(" hello", false),
|
|
||||||
(" sudo", false),
|
|
||||||
(" sudo ", true),
|
|
||||||
(" sudo ", true),
|
|
||||||
(" sudo ", true),
|
|
||||||
(" hello ", false),
|
|
||||||
(" hello | sudo ", true),
|
|
||||||
(" sudo|sudo", false),
|
|
||||||
("sudo | sudo ", true),
|
|
||||||
(" hello sud", false),
|
|
||||||
(" sudo | sud ", false),
|
|
||||||
(" sudo|sudo ", true),
|
|
||||||
(" sudo | sudo ls | sudo ", true),
|
|
||||||
];
|
|
||||||
for (idx, ele) in commands.iter().enumerate() {
|
|
||||||
let input = ele.0.as_bytes();
|
|
||||||
|
|
||||||
let mut engine_state = EngineState::new();
|
|
||||||
engine_state.add_file("test.nu".into(), vec![]);
|
|
||||||
|
|
||||||
let delta = {
|
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
|
||||||
let _ = working_set.add_file("child.nu".into(), input);
|
|
||||||
working_set.render()
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = engine_state.merge_delta(delta);
|
|
||||||
assert!(
|
|
||||||
result.is_ok(),
|
|
||||||
"Merge delta has failed: {}",
|
|
||||||
result.err().unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
let is_passthrough_command = is_passthrough_command(engine_state.get_file_contents());
|
|
||||||
assert_eq!(
|
|
||||||
is_passthrough_command, ele.1,
|
|
||||||
"index for '{}': {}",
|
|
||||||
ele.0, idx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -77,7 +77,10 @@ impl NuCompleter {
|
|||||||
Value::List {
|
Value::List {
|
||||||
vals: spans
|
vals: spans
|
||||||
.iter()
|
.iter()
|
||||||
.map(|it| Value::string(it, Span::unknown()))
|
.map(|it| Value::String {
|
||||||
|
val: it.to_string(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
},
|
},
|
||||||
@@ -89,7 +92,7 @@ impl NuCompleter {
|
|||||||
&self.engine_state,
|
&self.engine_state,
|
||||||
&mut callee_stack,
|
&mut callee_stack,
|
||||||
block,
|
block,
|
||||||
PipelineData::empty(),
|
PipelineData::new(span),
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
@@ -98,13 +101,19 @@ impl NuCompleter {
|
|||||||
Ok(pd) => {
|
Ok(pd) => {
|
||||||
let value = pd.into_value(span);
|
let value = pd.into_value(span);
|
||||||
if let Value::List { vals, span: _ } = value {
|
if let Value::List { vals, span: _ } = value {
|
||||||
let result =
|
let result = map_value_completions(
|
||||||
map_value_completions(vals.iter(), Span::new(span.start, span.end), offset);
|
vals.iter(),
|
||||||
|
Span {
|
||||||
|
start: span.start,
|
||||||
|
end: span.end,
|
||||||
|
},
|
||||||
|
offset,
|
||||||
|
);
|
||||||
|
|
||||||
return Some(result);
|
return Some(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => println!("failed to eval completer block: {err}"),
|
Err(err) => println!("failed to eval completer block: {}", err),
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
@@ -113,13 +122,14 @@ impl NuCompleter {
|
|||||||
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
|
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
|
||||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
let offset = working_set.next_span_start();
|
let offset = working_set.next_span_start();
|
||||||
|
let (mut new_line, alias_offset) = try_find_alias(line.as_bytes(), &working_set);
|
||||||
let initial_line = line.to_string();
|
let initial_line = line.to_string();
|
||||||
let mut line = line.to_string();
|
let alias_total_offset: usize = alias_offset.iter().sum();
|
||||||
line.insert(pos, 'a');
|
new_line.insert(alias_total_offset + pos, b'a');
|
||||||
let pos = offset + pos;
|
let pos = offset + pos;
|
||||||
let config = self.engine_state.get_config();
|
let config = self.engine_state.get_config();
|
||||||
|
|
||||||
let output = parse(&mut working_set, Some("completer"), line.as_bytes(), false);
|
let (output, _err) = parse(&mut working_set, Some("completer"), &new_line, false, &[]);
|
||||||
|
|
||||||
for pipeline in output.pipelines.into_iter() {
|
for pipeline in output.pipelines.into_iter() {
|
||||||
for pipeline_element in pipeline.elements {
|
for pipeline_element in pipeline.elements {
|
||||||
@@ -127,17 +137,12 @@ impl NuCompleter {
|
|||||||
PipelineElement::Expression(_, expr)
|
PipelineElement::Expression(_, expr)
|
||||||
| PipelineElement::Redirection(_, _, expr)
|
| PipelineElement::Redirection(_, _, expr)
|
||||||
| PipelineElement::And(_, expr)
|
| PipelineElement::And(_, expr)
|
||||||
| PipelineElement::Or(_, expr)
|
| PipelineElement::Or(_, expr) => {
|
||||||
| PipelineElement::SameTargetRedirection { cmd: (_, expr), .. }
|
|
||||||
| PipelineElement::SeparateRedirection { out: (_, expr), .. } => {
|
|
||||||
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
||||||
|
let span_offset: usize = alias_offset.iter().sum();
|
||||||
let mut spans: Vec<String> = vec![];
|
let mut spans: Vec<String> = vec![];
|
||||||
|
|
||||||
for (flat_idx, flat) in flattened.iter().enumerate() {
|
for (flat_idx, flat) in flattened.iter().enumerate() {
|
||||||
let is_passthrough_command = spans
|
|
||||||
.first()
|
|
||||||
.filter(|content| *content == &String::from("sudo"))
|
|
||||||
.is_some();
|
|
||||||
// Read the current spam to string
|
// Read the current spam to string
|
||||||
let current_span = working_set.get_span_contents(flat.0).to_vec();
|
let current_span = working_set.get_span_contents(flat.0).to_vec();
|
||||||
let current_span_str = String::from_utf8_lossy(¤t_span);
|
let current_span_str = String::from_utf8_lossy(¤t_span);
|
||||||
@@ -153,17 +158,27 @@ impl NuCompleter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Complete based on the last span
|
// Complete based on the last span
|
||||||
if pos >= flat.0.start && pos < flat.0.end {
|
if pos + span_offset >= flat.0.start && pos + span_offset < flat.0.end {
|
||||||
// Context variables
|
// Context variables
|
||||||
let most_left_var =
|
let most_left_var =
|
||||||
most_left_variable(flat_idx, &working_set, flattened.clone());
|
most_left_variable(flat_idx, &working_set, flattened.clone());
|
||||||
|
|
||||||
// Create a new span
|
// Create a new span
|
||||||
let new_span = Span::new(flat.0.start, flat.0.end - 1);
|
let new_span = if flat_idx == 0 {
|
||||||
|
Span {
|
||||||
|
start: flat.0.start,
|
||||||
|
end: flat.0.end - 1 - span_offset,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Span {
|
||||||
|
start: flat.0.start - span_offset,
|
||||||
|
end: flat.0.end - 1 - span_offset,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Parses the prefix. Completion should look up to the cursor position, not after.
|
// Parses the prefix. Completion should look up to the cursor position, not after.
|
||||||
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
|
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
|
||||||
let index = pos - flat.0.start;
|
let index = pos - (flat.0.start - span_offset);
|
||||||
prefix.drain(index..);
|
prefix.drain(index..);
|
||||||
|
|
||||||
// Variables completion
|
// Variables completion
|
||||||
@@ -213,9 +228,8 @@ impl NuCompleter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// specially check if it is currently empty - always complete commands
|
// specially check if it is currently empty - always complete commands
|
||||||
if (is_passthrough_command && flat_idx == 1)
|
if flat_idx == 0
|
||||||
|| (flat_idx == 0
|
&& working_set.get_span_contents(new_span).is_empty()
|
||||||
&& working_set.get_span_contents(new_span).is_empty())
|
|
||||||
{
|
{
|
||||||
let mut completer = CommandCompletion::new(
|
let mut completer = CommandCompletion::new(
|
||||||
self.engine_state.clone(),
|
self.engine_state.clone(),
|
||||||
@@ -236,7 +250,7 @@ impl NuCompleter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Completions that depends on the previous expression (e.g: use, source-env)
|
// Completions that depends on the previous expression (e.g: use, source-env)
|
||||||
if (is_passthrough_command && flat_idx > 1) || flat_idx > 0 {
|
if flat_idx > 0 {
|
||||||
if let Some(previous_expr) = flattened.get(flat_idx - 1) {
|
if let Some(previous_expr) = flattened.get(flat_idx - 1) {
|
||||||
// Read the content for the previous expression
|
// Read the content for the previous expression
|
||||||
let prev_expr_str =
|
let prev_expr_str =
|
||||||
@@ -383,6 +397,85 @@ impl ReedlineCompleter for NuCompleter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MatchedAlias = Vec<(Vec<u8>, Vec<u8>)>;
|
||||||
|
|
||||||
|
// Handler the completion when giving lines contains at least one alias. (e.g: `g checkout`)
|
||||||
|
// that `g` is an alias of `git`
|
||||||
|
fn try_find_alias(line: &[u8], working_set: &StateWorkingSet) -> (Vec<u8>, Vec<usize>) {
|
||||||
|
// An vector represents the offsets of alias
|
||||||
|
// e.g: the offset is 2 for the alias `g` of `git`
|
||||||
|
let mut alias_offset = vec![];
|
||||||
|
let mut output = vec![];
|
||||||
|
if let Some(matched_alias) = search_alias(line, working_set) {
|
||||||
|
let mut lens = matched_alias.len();
|
||||||
|
for (input_vec, line_vec) in matched_alias {
|
||||||
|
alias_offset.push(line_vec.len() - input_vec.len());
|
||||||
|
output.extend(line_vec);
|
||||||
|
if lens > 1 {
|
||||||
|
output.push(b' ');
|
||||||
|
lens -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !line.is_empty() {
|
||||||
|
let last = line.last().expect("input is empty");
|
||||||
|
if last == &b' ' {
|
||||||
|
output.push(b' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output = line.to_vec();
|
||||||
|
}
|
||||||
|
|
||||||
|
(output, alias_offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_alias(input: &[u8], working_set: &StateWorkingSet) -> Option<MatchedAlias> {
|
||||||
|
let mut vec_names = vec![];
|
||||||
|
let mut vec_alias = vec![];
|
||||||
|
let mut pos = 0;
|
||||||
|
let mut is_alias = false;
|
||||||
|
for (index, character) in input.iter().enumerate() {
|
||||||
|
if *character == b' ' {
|
||||||
|
let range = &input[pos..index];
|
||||||
|
vec_names.push(range.to_owned());
|
||||||
|
pos = index + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Push the rest to names vector.
|
||||||
|
if pos < input.len() {
|
||||||
|
vec_names.push(input[pos..].to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
for name in &vec_names {
|
||||||
|
if let Some(alias_id) = working_set.find_alias(&name[..]) {
|
||||||
|
let alias_span = working_set.get_alias(alias_id);
|
||||||
|
let mut span_vec = vec![];
|
||||||
|
is_alias = true;
|
||||||
|
for alias in alias_span {
|
||||||
|
let name = working_set.get_span_contents(*alias);
|
||||||
|
if !name.is_empty() {
|
||||||
|
span_vec.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Join span of vector together for complex alias, e.g: `f` is an alias for `git remote -v`
|
||||||
|
let full_aliases = span_vec.join(&[b' '][..]);
|
||||||
|
vec_alias.push(full_aliases);
|
||||||
|
} else {
|
||||||
|
vec_alias.push(name.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_alias {
|
||||||
|
// Zip names and alias vectors, the original inputs and its aliases mapping.
|
||||||
|
// e.g:(['g'], ['g','i','t'])
|
||||||
|
let output = vec_names.into_iter().zip(vec_alias).collect();
|
||||||
|
Some(output)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// reads the most left variable returning it's name (e.g: $myvar)
|
// reads the most left variable returning it's name (e.g: $myvar)
|
||||||
// and the depth (a.b.c)
|
// and the depth (a.b.c)
|
||||||
fn most_left_variable(
|
fn most_left_variable(
|
||||||
@@ -403,7 +496,7 @@ fn most_left_variable(
|
|||||||
let result = working_set.get_span_contents(item.0).to_vec();
|
let result = working_set.get_span_contents(item.0).to_vec();
|
||||||
|
|
||||||
match item.1 {
|
match item.1 {
|
||||||
FlatShape::Variable(_) => {
|
FlatShape::Variable => {
|
||||||
variables_found.push(result);
|
variables_found.push(result);
|
||||||
found_var = true;
|
found_var = true;
|
||||||
|
|
||||||
@@ -494,64 +587,3 @@ pub fn map_value_completions<'a>(
|
|||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod completer_tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_completion_helper() {
|
|
||||||
let mut engine_state =
|
|
||||||
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context());
|
|
||||||
|
|
||||||
// Custom additions
|
|
||||||
let delta = {
|
|
||||||
let working_set = nu_protocol::engine::StateWorkingSet::new(&engine_state);
|
|
||||||
working_set.render()
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = engine_state.merge_delta(delta);
|
|
||||||
assert!(
|
|
||||||
result.is_ok(),
|
|
||||||
"Error merging delta: {:?}",
|
|
||||||
result.err().unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(engine_state.into(), Stack::new());
|
|
||||||
let dataset = vec![
|
|
||||||
("sudo", false, "", Vec::new()),
|
|
||||||
("sudo l", true, "l", vec!["ls", "let", "lines", "loop"]),
|
|
||||||
(" sudo", false, "", Vec::new()),
|
|
||||||
(" sudo le", true, "le", vec!["let", "length"]),
|
|
||||||
(
|
|
||||||
"ls | c",
|
|
||||||
true,
|
|
||||||
"c",
|
|
||||||
vec!["cd", "config", "const", "cp", "cal"],
|
|
||||||
),
|
|
||||||
("ls | sudo m", true, "m", vec!["mv", "mut", "move"]),
|
|
||||||
];
|
|
||||||
for (line, has_result, begins_with, expected_values) in dataset {
|
|
||||||
let result = completer.completion_helper(line, line.len());
|
|
||||||
// Test whether the result is empty or not
|
|
||||||
assert_eq!(!result.is_empty(), has_result, "line: {}", line);
|
|
||||||
|
|
||||||
// Test whether the result begins with the expected value
|
|
||||||
result
|
|
||||||
.iter()
|
|
||||||
.for_each(|x| assert!(x.value.starts_with(begins_with)));
|
|
||||||
|
|
||||||
// Test whether the result contains all the expected values
|
|
||||||
assert_eq!(
|
|
||||||
result
|
|
||||||
.iter()
|
|
||||||
.map(|x| expected_values.contains(&x.value.as_str()))
|
|
||||||
.filter(|x| *x)
|
|
||||||
.count(),
|
|
||||||
expected_values.len(),
|
|
||||||
"line: {}",
|
|
||||||
line
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -6,7 +6,6 @@ use nu_protocol::{
|
|||||||
PipelineData, Span, Type, Value,
|
PipelineData, Span, Type, Value,
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::completer::map_value_completions;
|
use super::completer::map_value_completions;
|
||||||
@@ -53,13 +52,13 @@ impl Completer for CustomCompletion {
|
|||||||
head: span,
|
head: span,
|
||||||
arguments: vec![
|
arguments: vec![
|
||||||
Argument::Positional(Expression {
|
Argument::Positional(Expression {
|
||||||
span: Span::unknown(),
|
span: Span { start: 0, end: 0 },
|
||||||
ty: Type::String,
|
ty: Type::String,
|
||||||
expr: Expr::String(self.line.clone()),
|
expr: Expr::String(self.line.clone()),
|
||||||
custom_completion: None,
|
custom_completion: None,
|
||||||
}),
|
}),
|
||||||
Argument::Positional(Expression {
|
Argument::Positional(Expression {
|
||||||
span: Span::unknown(),
|
span: Span { start: 0, end: 0 },
|
||||||
ty: Type::Int,
|
ty: Type::Int,
|
||||||
expr: Expr::Int(line_pos as i64),
|
expr: Expr::Int(line_pos as i64),
|
||||||
custom_completion: None,
|
custom_completion: None,
|
||||||
@@ -67,16 +66,15 @@ impl Completer for CustomCompletion {
|
|||||||
],
|
],
|
||||||
redirect_stdout: true,
|
redirect_stdout: true,
|
||||||
redirect_stderr: true,
|
redirect_stderr: true,
|
||||||
parser_info: HashMap::new(),
|
|
||||||
},
|
},
|
||||||
PipelineData::empty(),
|
PipelineData::new(span),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut custom_completion_options = None;
|
let mut custom_completion_options = None;
|
||||||
|
|
||||||
// Parse result
|
// Parse result
|
||||||
let suggestions = result
|
let suggestions = match result {
|
||||||
.map(|pd| {
|
Ok(pd) => {
|
||||||
let value = pd.into_value(span);
|
let value = pd.into_value(span);
|
||||||
match &value {
|
match &value {
|
||||||
Value::Record { .. } => {
|
Value::Record { .. } => {
|
||||||
@@ -133,8 +131,9 @@ impl Completer for CustomCompletion {
|
|||||||
Value::List { vals, .. } => map_value_completions(vals.iter(), span, offset),
|
Value::List { vals, .. } => map_value_completions(vals.iter(), span, offset),
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.unwrap_or_default();
|
_ => vec![],
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(custom_completion_options) = custom_completion_options {
|
if let Some(custom_completion_options) = custom_completion_options {
|
||||||
filter(&prefix, suggestions, &custom_completion_options)
|
filter(&prefix, suggestions, &custom_completion_options)
|
||||||
|
@@ -8,7 +8,7 @@ use std::fs;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::{partial_from, prepend_base_dir, SortBy};
|
use super::{partial_from, prepend_base_dir};
|
||||||
|
|
||||||
const SEP: char = std::path::MAIN_SEPARATOR;
|
const SEP: char = std::path::MAIN_SEPARATOR;
|
||||||
|
|
||||||
@@ -33,7 +33,14 @@ impl Completer for DirectoryCompletion {
|
|||||||
_: usize,
|
_: usize,
|
||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
let cwd = self.engine_state.current_work_dir();
|
let cwd = if let Some(d) = self.engine_state.get_env_var("PWD") {
|
||||||
|
match d.as_string() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => "".to_string(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
};
|
||||||
let partial = String::from_utf8_lossy(&prefix).to_string();
|
let partial = String::from_utf8_lossy(&prefix).to_string();
|
||||||
|
|
||||||
// Filter only the folders
|
// Filter only the folders
|
||||||
@@ -60,20 +67,12 @@ impl Completer for DirectoryCompletion {
|
|||||||
|
|
||||||
// Sort items
|
// Sort items
|
||||||
let mut sorted_items = items;
|
let mut sorted_items = items;
|
||||||
|
sorted_items.sort_by(|a, b| a.value.cmp(&b.value));
|
||||||
match self.get_sort_by() {
|
sorted_items.sort_by(|a, b| {
|
||||||
SortBy::Ascending => {
|
let a_distance = levenshtein_distance(&prefix_str, &a.value);
|
||||||
sorted_items.sort_by(|a, b| a.value.cmp(&b.value));
|
let b_distance = levenshtein_distance(&prefix_str, &b.value);
|
||||||
}
|
a_distance.cmp(&b_distance)
|
||||||
SortBy::LevenshteinDistance => {
|
});
|
||||||
sorted_items.sort_by(|a, b| {
|
|
||||||
let a_distance = levenshtein_distance(&prefix_str, &a.value);
|
|
||||||
let b_distance = levenshtein_distance(&prefix_str, &b.value);
|
|
||||||
a_distance.cmp(&b_distance)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Separate the results between hidden and non hidden
|
// Separate the results between hidden and non hidden
|
||||||
let mut hidden: Vec<Suggestion> = vec![];
|
let mut hidden: Vec<Suggestion> = vec![];
|
||||||
@@ -127,7 +126,7 @@ pub fn directory_completion(
|
|||||||
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
||||||
if matches(&partial, &file_name, options) {
|
if matches(&partial, &file_name, options) {
|
||||||
let mut path = if prepend_base_dir(original_input, &base_dir_name) {
|
let mut path = if prepend_base_dir(original_input, &base_dir_name) {
|
||||||
format!("{base_dir_name}{file_name}")
|
format!("{}{}", base_dir_name, file_name)
|
||||||
} else {
|
} else {
|
||||||
file_name.to_string()
|
file_name.to_string()
|
||||||
};
|
};
|
||||||
@@ -137,13 +136,9 @@ pub fn directory_completion(
|
|||||||
file_name.push(SEP);
|
file_name.push(SEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix files or folders with quotes or hash
|
// Fix files or folders with quotes
|
||||||
if path.contains('\'')
|
if path.contains('\'') || path.contains('"') || path.contains(' ') {
|
||||||
|| path.contains('"')
|
path = format!("`{}`", path);
|
||||||
|| path.contains(' ')
|
|
||||||
|| path.contains('#')
|
|
||||||
{
|
|
||||||
path = format!("`{path}`");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((span, path))
|
Some((span, path))
|
||||||
|
@@ -58,7 +58,7 @@ impl Completer for DotNuCompletion {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Check if the base_dir is a folder
|
// Check if the base_dir is a folder
|
||||||
if base_dir != format!(".{SEP}") {
|
if base_dir != format!(".{}", SEP) {
|
||||||
// Add the base dir into the directories to be searched
|
// Add the base dir into the directories to be searched
|
||||||
search_dirs.push(base_dir.clone());
|
search_dirs.push(base_dir.clone());
|
||||||
|
|
||||||
@@ -70,7 +70,14 @@ impl Completer for DotNuCompletion {
|
|||||||
partial = base_dir_partial;
|
partial = base_dir_partial;
|
||||||
} else {
|
} else {
|
||||||
// Fetch the current folder
|
// Fetch the current folder
|
||||||
let current_folder = self.engine_state.current_work_dir();
|
let current_folder = if let Some(d) = self.engine_state.get_env_var("PWD") {
|
||||||
|
match d.as_string() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => "".to_string(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
};
|
||||||
is_current_folder = true;
|
is_current_folder = true;
|
||||||
|
|
||||||
// Add the current folder and the lib dirs into the
|
// Add the current folder and the lib dirs into the
|
||||||
|
@@ -7,8 +7,6 @@ use reedline::Suggestion;
|
|||||||
use std::path::{is_separator, Path};
|
use std::path::{is_separator, Path};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::SortBy;
|
|
||||||
|
|
||||||
const SEP: char = std::path::MAIN_SEPARATOR;
|
const SEP: char = std::path::MAIN_SEPARATOR;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -32,7 +30,14 @@ impl Completer for FileCompletion {
|
|||||||
_: usize,
|
_: usize,
|
||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
let cwd = self.engine_state.current_work_dir();
|
let cwd = if let Some(d) = self.engine_state.get_env_var("PWD") {
|
||||||
|
match d.as_string() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => "".to_string(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
};
|
||||||
let prefix = String::from_utf8_lossy(&prefix).to_string();
|
let prefix = String::from_utf8_lossy(&prefix).to_string();
|
||||||
let output: Vec<_> = file_path_completion(span, &prefix, &cwd, options)
|
let output: Vec<_> = file_path_completion(span, &prefix, &cwd, options)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -57,20 +62,12 @@ impl Completer for FileCompletion {
|
|||||||
|
|
||||||
// Sort items
|
// Sort items
|
||||||
let mut sorted_items = items;
|
let mut sorted_items = items;
|
||||||
|
sorted_items.sort_by(|a, b| a.value.cmp(&b.value));
|
||||||
match self.get_sort_by() {
|
sorted_items.sort_by(|a, b| {
|
||||||
SortBy::Ascending => {
|
let a_distance = levenshtein_distance(&prefix_str, &a.value);
|
||||||
sorted_items.sort_by(|a, b| a.value.cmp(&b.value));
|
let b_distance = levenshtein_distance(&prefix_str, &b.value);
|
||||||
}
|
a_distance.cmp(&b_distance)
|
||||||
SortBy::LevenshteinDistance => {
|
});
|
||||||
sorted_items.sort_by(|a, b| {
|
|
||||||
let a_distance = levenshtein_distance(&prefix_str, &a.value);
|
|
||||||
let b_distance = levenshtein_distance(&prefix_str, &b.value);
|
|
||||||
a_distance.cmp(&b_distance)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Separate the results between hidden and non hidden
|
// Separate the results between hidden and non hidden
|
||||||
let mut hidden: Vec<Suggestion> = vec![];
|
let mut hidden: Vec<Suggestion> = vec![];
|
||||||
@@ -134,7 +131,7 @@ pub fn file_path_completion(
|
|||||||
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
||||||
if matches(&partial, &file_name, options) {
|
if matches(&partial, &file_name, options) {
|
||||||
let mut path = if prepend_base_dir(original_input, &base_dir_name) {
|
let mut path = if prepend_base_dir(original_input, &base_dir_name) {
|
||||||
format!("{base_dir_name}{file_name}")
|
format!("{}{}", base_dir_name, file_name)
|
||||||
} else {
|
} else {
|
||||||
file_name.to_string()
|
file_name.to_string()
|
||||||
};
|
};
|
||||||
@@ -144,25 +141,9 @@ pub fn file_path_completion(
|
|||||||
file_name.push(SEP);
|
file_name.push(SEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix files or folders with quotes or hashes
|
// Fix files or folders with quotes
|
||||||
if path.contains('\'')
|
if path.contains('\'') || path.contains('"') || path.contains(' ') {
|
||||||
|| path.contains('"')
|
path = format!("`{}`", path);
|
||||||
|| path.contains(' ')
|
|
||||||
|| path.contains('#')
|
|
||||||
|| path.contains('(')
|
|
||||||
|| path.contains(')')
|
|
||||||
|| path.starts_with('0')
|
|
||||||
|| path.starts_with('1')
|
|
||||||
|| path.starts_with('2')
|
|
||||||
|| path.starts_with('3')
|
|
||||||
|| path.starts_with('4')
|
|
||||||
|| path.starts_with('5')
|
|
||||||
|| path.starts_with('6')
|
|
||||||
|| path.starts_with('7')
|
|
||||||
|| path.starts_with('8')
|
|
||||||
|| path.starts_with('9')
|
|
||||||
{
|
|
||||||
path = format!("`{path}`");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((span, path))
|
Some((span, path))
|
||||||
@@ -190,7 +171,7 @@ pub fn matches(partial: &str, from: &str, options: &CompletionOptions) -> bool {
|
|||||||
|
|
||||||
/// Returns whether the base_dir should be prepended to the file path
|
/// Returns whether the base_dir should be prepended to the file path
|
||||||
pub fn prepend_base_dir(input: &str, base_dir: &str) -> bool {
|
pub fn prepend_base_dir(input: &str, base_dir: &str) -> bool {
|
||||||
if base_dir == format!(".{SEP}") {
|
if base_dir == format!(".{}", SEP) {
|
||||||
// if the current base_dir path is the local folder we only add a "./" prefix if the user
|
// if the current base_dir path is the local folder we only add a "./" prefix if the user
|
||||||
// input already includes a local folder prefix.
|
// input already includes a local folder prefix.
|
||||||
let manually_entered = {
|
let manually_entered = {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
use crate::completions::{Completer, CompletionOptions};
|
use crate::completions::{Completer, CompletionOptions};
|
||||||
use nu_engine::{column::get_columns, eval_variable};
|
use nu_engine::eval_variable;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
Span, Value,
|
Span, Value,
|
||||||
@@ -9,8 +9,6 @@ use reedline::Suggestion;
|
|||||||
use std::str;
|
use std::str;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::MatchAlgorithm;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct VariableCompletion {
|
pub struct VariableCompletion {
|
||||||
engine_state: Arc<EngineState>, // TODO: Is engine state necessary? It's already a part of working set in fetch()
|
engine_state: Arc<EngineState>, // TODO: Is engine state necessary? It's already a part of working set in fetch()
|
||||||
@@ -75,11 +73,10 @@ impl Completer for VariableCompletion {
|
|||||||
for suggestion in
|
for suggestion in
|
||||||
nested_suggestions(val.clone(), nested_levels, current_span)
|
nested_suggestions(val.clone(), nested_levels, current_span)
|
||||||
{
|
{
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options
|
||||||
options.case_sensitive,
|
.match_algorithm
|
||||||
suggestion.value.as_bytes(),
|
.matches_u8(suggestion.value.as_bytes(), &prefix)
|
||||||
&prefix,
|
{
|
||||||
) {
|
|
||||||
output.push(suggestion);
|
output.push(suggestion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,11 +86,10 @@ impl Completer for VariableCompletion {
|
|||||||
} else {
|
} else {
|
||||||
// No nesting provided, return all env vars
|
// No nesting provided, return all env vars
|
||||||
for env_var in env_vars {
|
for env_var in env_vars {
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options
|
||||||
options.case_sensitive,
|
.match_algorithm
|
||||||
env_var.0.as_bytes(),
|
.matches_u8(env_var.0.as_bytes(), &prefix)
|
||||||
&prefix,
|
{
|
||||||
) {
|
|
||||||
output.push(Suggestion {
|
output.push(Suggestion {
|
||||||
value: env_var.0,
|
value: env_var.0,
|
||||||
description: None,
|
description: None,
|
||||||
@@ -115,16 +111,18 @@ impl Completer for VariableCompletion {
|
|||||||
&self.engine_state,
|
&self.engine_state,
|
||||||
&self.stack,
|
&self.stack,
|
||||||
nu_protocol::NU_VARIABLE_ID,
|
nu_protocol::NU_VARIABLE_ID,
|
||||||
nu_protocol::Span::new(current_span.start, current_span.end),
|
nu_protocol::Span {
|
||||||
|
start: current_span.start,
|
||||||
|
end: current_span.end,
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
for suggestion in
|
for suggestion in
|
||||||
nested_suggestions(nuval, self.var_context.1.clone(), current_span)
|
nested_suggestions(nuval, self.var_context.1.clone(), current_span)
|
||||||
{
|
{
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options
|
||||||
options.case_sensitive,
|
.match_algorithm
|
||||||
suggestion.value.as_bytes(),
|
.matches_u8(suggestion.value.as_bytes(), &prefix)
|
||||||
&prefix,
|
{
|
||||||
) {
|
|
||||||
output.push(suggestion);
|
output.push(suggestion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,18 +134,23 @@ impl Completer for VariableCompletion {
|
|||||||
// Completion other variable types
|
// Completion other variable types
|
||||||
if let Some(var_id) = var_id {
|
if let Some(var_id) = var_id {
|
||||||
// Extract the variable value from the stack
|
// Extract the variable value from the stack
|
||||||
let var = self.stack.get_var(var_id, Span::new(span.start, span.end));
|
let var = self.stack.get_var(
|
||||||
|
var_id,
|
||||||
|
Span {
|
||||||
|
start: span.start,
|
||||||
|
end: span.end,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// If the value exists and it's of type Record
|
// If the value exists and it's of type Record
|
||||||
if let Ok(value) = var {
|
if let Ok(value) = var {
|
||||||
for suggestion in
|
for suggestion in
|
||||||
nested_suggestions(value, self.var_context.1.clone(), current_span)
|
nested_suggestions(value, self.var_context.1.clone(), current_span)
|
||||||
{
|
{
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options
|
||||||
options.case_sensitive,
|
.match_algorithm
|
||||||
suggestion.value.as_bytes(),
|
.matches_u8(suggestion.value.as_bytes(), &prefix)
|
||||||
&prefix,
|
{
|
||||||
) {
|
|
||||||
output.push(suggestion);
|
output.push(suggestion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,11 +162,10 @@ impl Completer for VariableCompletion {
|
|||||||
|
|
||||||
// Variable completion (e.g: $en<tab> to complete $env)
|
// Variable completion (e.g: $en<tab> to complete $env)
|
||||||
for builtin in builtins {
|
for builtin in builtins {
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options
|
||||||
options.case_sensitive,
|
.match_algorithm
|
||||||
builtin.as_bytes(),
|
.matches_u8(builtin.as_bytes(), &prefix)
|
||||||
&prefix,
|
{
|
||||||
) {
|
|
||||||
output.push(Suggestion {
|
output.push(Suggestion {
|
||||||
value: builtin.to_string(),
|
value: builtin.to_string(),
|
||||||
description: None,
|
description: None,
|
||||||
@@ -179,13 +181,13 @@ impl Completer for VariableCompletion {
|
|||||||
let mut removed_overlays = vec![];
|
let mut removed_overlays = vec![];
|
||||||
// Working set scope vars
|
// Working set scope vars
|
||||||
for scope_frame in working_set.delta.scope.iter().rev() {
|
for scope_frame in working_set.delta.scope.iter().rev() {
|
||||||
for overlay_frame in scope_frame.active_overlays(&mut removed_overlays).rev() {
|
for overlay_frame in scope_frame
|
||||||
|
.active_overlays(&mut removed_overlays)
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
{
|
||||||
for v in &overlay_frame.vars {
|
for v in &overlay_frame.vars {
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options.match_algorithm.matches_u8(v.0, &prefix) {
|
||||||
options.case_sensitive,
|
|
||||||
v.0,
|
|
||||||
&prefix,
|
|
||||||
) {
|
|
||||||
output.push(Suggestion {
|
output.push(Suggestion {
|
||||||
value: String::from_utf8_lossy(v.0).to_string(),
|
value: String::from_utf8_lossy(v.0).to_string(),
|
||||||
description: None,
|
description: None,
|
||||||
@@ -200,13 +202,14 @@ impl Completer for VariableCompletion {
|
|||||||
|
|
||||||
// Permanent state vars
|
// Permanent state vars
|
||||||
// for scope in &self.engine_state.scope {
|
// for scope in &self.engine_state.scope {
|
||||||
for overlay_frame in self.engine_state.active_overlays(&removed_overlays).rev() {
|
for overlay_frame in self
|
||||||
|
.engine_state
|
||||||
|
.active_overlays(&removed_overlays)
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
{
|
||||||
for v in &overlay_frame.vars {
|
for v in &overlay_frame.vars {
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options.match_algorithm.matches_u8(v.0, &prefix) {
|
||||||
options.case_sensitive,
|
|
||||||
v.0,
|
|
||||||
&prefix,
|
|
||||||
) {
|
|
||||||
output.push(Suggestion {
|
output.push(Suggestion {
|
||||||
value: String::from_utf8_lossy(v.0).to_string(),
|
value: String::from_utf8_lossy(v.0).to_string(),
|
||||||
description: None,
|
description: None,
|
||||||
@@ -253,33 +256,7 @@ fn nested_suggestions(
|
|||||||
|
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
Value::LazyRecord { val, .. } => {
|
|
||||||
// Add all the columns as completion
|
|
||||||
for column_name in val.column_names() {
|
|
||||||
output.push(Suggestion {
|
|
||||||
value: column_name.to_string(),
|
|
||||||
description: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
|
||||||
append_whitespace: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
output
|
|
||||||
}
|
|
||||||
Value::List { vals, span: _ } => {
|
|
||||||
for column_name in get_columns(vals.as_slice()) {
|
|
||||||
output.push(Suggestion {
|
|
||||||
value: column_name,
|
|
||||||
description: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
|
||||||
append_whitespace: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
output
|
|
||||||
}
|
|
||||||
_ => output,
|
_ => output,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,39 +281,7 @@ fn recursive_value(val: Value, sublevels: Vec<Vec<u8>>) -> Value {
|
|||||||
|
|
||||||
// Current sublevel value not found
|
// Current sublevel value not found
|
||||||
return Value::Nothing {
|
return Value::Nothing {
|
||||||
span: Span::unknown(),
|
span: Span { start: 0, end: 0 },
|
||||||
};
|
|
||||||
}
|
|
||||||
Value::LazyRecord { val, span: _ } => {
|
|
||||||
for col in val.column_names() {
|
|
||||||
if col.as_bytes().to_vec() == next_sublevel {
|
|
||||||
return recursive_value(
|
|
||||||
val.get_column_value(col).unwrap_or_default(),
|
|
||||||
sublevels.into_iter().skip(1).collect(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current sublevel value not found
|
|
||||||
return Value::Nothing {
|
|
||||||
span: Span::unknown(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Value::List { vals, span } => {
|
|
||||||
for col in get_columns(vals.as_slice()) {
|
|
||||||
if col.as_bytes().to_vec() == next_sublevel {
|
|
||||||
return recursive_value(
|
|
||||||
Value::List { vals, span }
|
|
||||||
.get_data_by_key(&col)
|
|
||||||
.unwrap_or_default(),
|
|
||||||
sublevels.into_iter().skip(1).collect(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current sublevel value not found
|
|
||||||
return Value::Nothing {
|
|
||||||
span: Span::unknown(),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_ => return val,
|
_ => return val,
|
||||||
@@ -345,13 +290,3 @@ fn recursive_value(val: Value, sublevels: Vec<Vec<u8>>) -> Value {
|
|||||||
|
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MatchAlgorithm {
|
|
||||||
pub fn matches_u8_insensitive(&self, sensitive: bool, haystack: &[u8], needle: &[u8]) -> bool {
|
|
||||||
if sensitive {
|
|
||||||
self.matches_u8(haystack, needle)
|
|
||||||
} else {
|
|
||||||
self.matches_u8(&haystack.to_ascii_lowercase(), &needle.to_ascii_lowercase())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
use crate::util::eval_source;
|
use crate::util::{eval_source, report_error};
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
|
use log::info;
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
|
use nu_parser::ParseError;
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
|
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
|
||||||
use nu_protocol::report_error;
|
|
||||||
use nu_protocol::{HistoryFileFormat, PipelineData};
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use nu_protocol::{ParseError, Spanned};
|
use nu_protocol::Spanned;
|
||||||
#[cfg(feature = "plugin")]
|
use nu_protocol::{HistoryFileFormat, PipelineData, Span};
|
||||||
use nu_utils::utils::perf;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
@@ -23,8 +24,6 @@ pub fn read_plugin_file(
|
|||||||
plugin_file: Option<Spanned<String>>,
|
plugin_file: Option<Spanned<String>>,
|
||||||
storage_path: &str,
|
storage_path: &str,
|
||||||
) {
|
) {
|
||||||
let start_time = std::time::Instant::now();
|
|
||||||
let mut plug_path = String::new();
|
|
||||||
// Reading signatures from signature file
|
// Reading signatures from signature file
|
||||||
// The plugin.nu file stores the parsed signature collected from each registered plugin
|
// The plugin.nu file stores the parsed signature collected from each registered plugin
|
||||||
add_plugin_file(engine_state, plugin_file, storage_path);
|
add_plugin_file(engine_state, plugin_file, storage_path);
|
||||||
@@ -32,27 +31,19 @@ pub fn read_plugin_file(
|
|||||||
let plugin_path = engine_state.plugin_signatures.clone();
|
let plugin_path = engine_state.plugin_signatures.clone();
|
||||||
if let Some(plugin_path) = plugin_path {
|
if let Some(plugin_path) = plugin_path {
|
||||||
let plugin_filename = plugin_path.to_string_lossy();
|
let plugin_filename = plugin_path.to_string_lossy();
|
||||||
plug_path = plugin_filename.to_string();
|
|
||||||
if let Ok(contents) = std::fs::read(&plugin_path) {
|
if let Ok(contents) = std::fs::read(&plugin_path) {
|
||||||
eval_source(
|
eval_source(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
&contents,
|
&contents,
|
||||||
&plugin_filename,
|
&plugin_filename,
|
||||||
PipelineData::empty(),
|
PipelineData::new(Span::new(0, 0)),
|
||||||
false,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
perf(
|
info!("read_plugin_file {}:{}:{}", file!(), line!(), column!());
|
||||||
&format!("read_plugin_file {}", &plug_path),
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
engine_state.get_config().use_ansi_coloring,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
@@ -65,11 +56,12 @@ pub fn add_plugin_file(
|
|||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
let cwd = working_set.get_cwd();
|
let cwd = working_set.get_cwd();
|
||||||
|
|
||||||
if let Ok(path) = canonicalize_with(&plugin_file.item, cwd) {
|
match canonicalize_with(&plugin_file.item, cwd) {
|
||||||
engine_state.plugin_signatures = Some(path)
|
Ok(path) => engine_state.plugin_signatures = Some(path),
|
||||||
} else {
|
Err(_) => {
|
||||||
let e = ParseError::FileNotFound(plugin_file.item, plugin_file.span);
|
let e = ParseError::FileNotFound(plugin_file.item, plugin_file.span);
|
||||||
report_error(&working_set, &e);
|
report_error(&working_set, &e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(mut plugin_path) = nu_path::config_dir() {
|
} else if let Some(mut plugin_path) = nu_path::config_dir() {
|
||||||
// Path to store plugins signatures
|
// Path to store plugins signatures
|
||||||
@@ -93,8 +85,7 @@ pub fn eval_config_contents(
|
|||||||
stack,
|
stack,
|
||||||
&contents,
|
&contents,
|
||||||
&config_filename,
|
&config_filename,
|
||||||
PipelineData::empty(),
|
PipelineData::new(Span::new(0, 0)),
|
||||||
false,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Merge the environment in case env vars changed in the config
|
// Merge the environment in case env vars changed in the config
|
||||||
|
@@ -1,15 +1,14 @@
|
|||||||
use crate::util::eval_source;
|
use crate::util::{eval_source, report_error};
|
||||||
use log::info;
|
use log::info;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
use nu_engine::{convert_env_values, current_dir};
|
use nu_engine::convert_env_values;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_path::canonicalize_with;
|
use nu_protocol::Type;
|
||||||
use nu_protocol::report_error;
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
Config, PipelineData, ShellError, Span, Type, Value,
|
Config, PipelineData, Span, Value,
|
||||||
};
|
};
|
||||||
use nu_utils::stdout_write_all_and_flush;
|
use nu_utils::stdout_write_all_and_flush;
|
||||||
|
|
||||||
@@ -28,79 +27,14 @@ pub fn evaluate_file(
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let file = std::fs::read(&path).into_diagnostic()?;
|
||||||
|
|
||||||
let file_path = canonicalize_with(&path, cwd).unwrap_or_else(|e| {
|
engine_state.start_in_file(Some(&path));
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
report_error(
|
|
||||||
&working_set,
|
|
||||||
&ShellError::FileNotFoundCustom(
|
|
||||||
format!("Could not access file '{}': {:?}", path, e.to_string()),
|
|
||||||
Span::unknown(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
std::process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
let file_path_str = file_path.to_str().unwrap_or_else(|| {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
report_error(
|
|
||||||
&working_set,
|
|
||||||
&ShellError::NonUtf8Custom(
|
|
||||||
format!(
|
|
||||||
"Input file name '{}' is not valid UTF8",
|
|
||||||
file_path.to_string_lossy()
|
|
||||||
),
|
|
||||||
Span::unknown(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
std::process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
let file = std::fs::read(&file_path)
|
|
||||||
.into_diagnostic()
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
report_error(
|
|
||||||
&working_set,
|
|
||||||
&ShellError::FileNotFoundCustom(
|
|
||||||
format!(
|
|
||||||
"Could not read file '{}': {:?}",
|
|
||||||
file_path_str,
|
|
||||||
e.to_string()
|
|
||||||
),
|
|
||||||
Span::unknown(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
std::process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
engine_state.start_in_file(Some(file_path_str));
|
|
||||||
|
|
||||||
let parent = file_path.parent().unwrap_or_else(|| {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
report_error(
|
|
||||||
&working_set,
|
|
||||||
&ShellError::FileNotFoundCustom(
|
|
||||||
format!("The file path '{file_path_str}' does not have a parent"),
|
|
||||||
Span::unknown(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
std::process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
stack.add_env_var(
|
|
||||||
"FILE_PWD".to_string(),
|
|
||||||
Value::string(parent.to_string_lossy(), Span::unknown()),
|
|
||||||
);
|
|
||||||
stack.add_env_var(
|
|
||||||
"CURRENT_FILE".to_string(),
|
|
||||||
Value::string(file_path.to_string_lossy(), Span::unknown()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
trace!("parsing file: {}", file_path_str);
|
trace!("parsing file: {}", path);
|
||||||
let _ = parse(&mut working_set, Some(file_path_str), &file, false);
|
|
||||||
|
let _ = parse(&mut working_set, Some(&path), &file, false, &[]);
|
||||||
|
|
||||||
if working_set.find_decl(b"main", &Type::Any).is_some() {
|
if working_set.find_decl(b"main", &Type::Any).is_some() {
|
||||||
let args = format!("main {}", args.join(" "));
|
let args = format!("main {}", args.join(" "));
|
||||||
@@ -109,23 +43,15 @@ pub fn evaluate_file(
|
|||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
&file,
|
&file,
|
||||||
file_path_str,
|
&path,
|
||||||
PipelineData::empty(),
|
PipelineData::new(Span::new(0, 0)),
|
||||||
true,
|
|
||||||
) {
|
) {
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
if !eval_source(
|
if !eval_source(engine_state, stack, args.as_bytes(), "<commandline>", input) {
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
args.as_bytes(),
|
|
||||||
"<commandline>",
|
|
||||||
input,
|
|
||||||
true,
|
|
||||||
) {
|
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
} else if !eval_source(engine_state, stack, &file, file_path_str, input, true) {
|
} else if !eval_source(engine_state, stack, &file, &path, input) {
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +60,7 @@ pub fn evaluate_file(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn print_table_or_error(
|
pub fn print_table_or_error(
|
||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
mut pipeline_data: PipelineData,
|
mut pipeline_data: PipelineData,
|
||||||
@@ -150,34 +76,43 @@ pub(crate) fn print_table_or_error(
|
|||||||
|
|
||||||
if let PipelineData::Value(Value::Error { error }, ..) = &pipeline_data {
|
if let PipelineData::Value(Value::Error { error }, ..) = &pipeline_data {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(&working_set, &**error);
|
|
||||||
|
report_error(&working_set, error);
|
||||||
|
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(decl_id) = engine_state.find_decl("table".as_bytes(), &[]) {
|
match engine_state.find_decl("table".as_bytes(), &[]) {
|
||||||
let command = engine_state.get_decl(decl_id);
|
Some(decl_id) => {
|
||||||
if command.get_block_id().is_some() {
|
let command = engine_state.get_decl(decl_id);
|
||||||
print_or_exit(pipeline_data, engine_state, config);
|
if command.get_block_id().is_some() {
|
||||||
} else {
|
print_or_exit(pipeline_data, engine_state, config);
|
||||||
// The final call on table command, it's ok to set redirect_output to false.
|
} else {
|
||||||
let mut call = Call::new(Span::new(0, 0));
|
let table = command.run(
|
||||||
call.redirect_stdout = false;
|
engine_state,
|
||||||
let table = command.run(engine_state, stack, &call, pipeline_data);
|
stack,
|
||||||
|
&Call::new(Span::new(0, 0)),
|
||||||
|
pipeline_data,
|
||||||
|
);
|
||||||
|
|
||||||
match table {
|
match table {
|
||||||
Ok(table) => {
|
Ok(table) => {
|
||||||
print_or_exit(table, engine_state, config);
|
print_or_exit(table, engine_state, config);
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(&working_set, &error);
|
|
||||||
std::process::exit(1);
|
report_error(&working_set, &error);
|
||||||
|
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
None => {
|
||||||
print_or_exit(pipeline_data, engine_state, config);
|
print_or_exit(pipeline_data, engine_state, config);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Make sure everything has finished
|
// Make sure everything has finished
|
||||||
if let Some(exit_code) = exit_code {
|
if let Some(exit_code) = exit_code {
|
||||||
@@ -198,12 +133,14 @@ fn print_or_exit(pipeline_data: PipelineData, engine_state: &mut EngineState, co
|
|||||||
if let Value::Error { error } = item {
|
if let Value::Error { error } = item {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
report_error(&working_set, &*error);
|
report_error(&working_set, &error);
|
||||||
|
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let out = item.into_string("\n", config) + "\n";
|
let mut out = item.into_string("\n", config);
|
||||||
let _ = stdout_write_all_and_flush(out).map_err(|err| eprintln!("{err}"));
|
out.push('\n');
|
||||||
|
|
||||||
|
let _ = stdout_write_all_and_flush(out).map_err(|err| eprintln!("{}", err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
mod commands;
|
mod commands;
|
||||||
mod completions;
|
mod completions;
|
||||||
mod config_files;
|
mod config_files;
|
||||||
mod eval_cmds;
|
|
||||||
mod eval_file;
|
mod eval_file;
|
||||||
mod menus;
|
mod menus;
|
||||||
mod nu_highlight;
|
mod nu_highlight;
|
||||||
@@ -14,19 +13,18 @@ mod syntax_highlight;
|
|||||||
mod util;
|
mod util;
|
||||||
mod validation;
|
mod validation;
|
||||||
|
|
||||||
pub use commands::add_cli_context;
|
pub use commands::evaluate_commands;
|
||||||
pub use completions::{FileCompletion, NuCompleter};
|
pub use completions::{FileCompletion, NuCompleter};
|
||||||
pub use config_files::eval_config_contents;
|
pub use config_files::eval_config_contents;
|
||||||
pub use eval_cmds::evaluate_commands;
|
|
||||||
pub use eval_file::evaluate_file;
|
pub use eval_file::evaluate_file;
|
||||||
pub use menus::{DescriptionMenu, NuHelpCompleter};
|
pub use menus::{DescriptionMenu, NuHelpCompleter};
|
||||||
pub use nu_cmd_base::util::get_init_cwd;
|
|
||||||
pub use nu_highlight::NuHighlight;
|
pub use nu_highlight::NuHighlight;
|
||||||
pub use print::Print;
|
pub use print::Print;
|
||||||
pub use prompt::NushellPrompt;
|
pub use prompt::NushellPrompt;
|
||||||
pub use repl::evaluate_repl;
|
pub use repl::evaluate_repl;
|
||||||
|
pub use repl::{eval_env_change_hook, eval_hook};
|
||||||
pub use syntax_highlight::NuHighlighter;
|
pub use syntax_highlight::NuHighlighter;
|
||||||
pub use util::{eval_source, gather_parent_env_vars};
|
pub use util::{eval_source, gather_parent_env_vars, get_init_cwd, report_error, report_error_new};
|
||||||
pub use validation::NuValidator;
|
pub use validation::NuValidator;
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
|
@@ -411,10 +411,10 @@ impl DescriptionMenu {
|
|||||||
RESET
|
RESET
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(" {example}\r\n")
|
format!(" {}\r\n", example)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
format!(" {example}\r\n")
|
format!(" {}\r\n", example)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@@ -429,7 +429,7 @@ impl DescriptionMenu {
|
|||||||
examples,
|
examples,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!("\r\n\r\nExamples:\r\n{examples}",)
|
format!("\r\n\r\nExamples:\r\n{}", examples,)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -57,9 +57,7 @@ impl NuHelpCompleter {
|
|||||||
let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature());
|
let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature());
|
||||||
|
|
||||||
if !sig.named.is_empty() {
|
if !sig.named.is_empty() {
|
||||||
long_desc.push_str(&get_flags_section(sig, |v| {
|
long_desc.push_str(&get_flags_section(sig))
|
||||||
v.into_string_parsable(", ", &self.0.config)
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sig.required_positional.is_empty()
|
if !sig.required_positional.is_empty()
|
||||||
@@ -71,18 +69,10 @@ impl NuHelpCompleter {
|
|||||||
let _ = write!(long_desc, " {}: {}\r\n", positional.name, positional.desc);
|
let _ = write!(long_desc, " {}: {}\r\n", positional.name, positional.desc);
|
||||||
}
|
}
|
||||||
for positional in &sig.optional_positional {
|
for positional in &sig.optional_positional {
|
||||||
let opt_suffix = if let Some(value) = &positional.default_value {
|
|
||||||
format!(
|
|
||||||
" (optional, default: {})",
|
|
||||||
&value.into_string_parsable(", ", &self.0.config),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(" (optional)").to_string()
|
|
||||||
};
|
|
||||||
let _ = write!(
|
let _ = write!(
|
||||||
long_desc,
|
long_desc,
|
||||||
" (optional) {}: {}{}\r\n",
|
" (optional) {}: {}\r\n",
|
||||||
positional.name, positional.desc, opt_suffix
|
positional.name, positional.desc
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -42,14 +42,20 @@ impl Completer for NuMenuCompleter {
|
|||||||
|
|
||||||
if let Some(buffer) = block.signature.get_positional(0) {
|
if let Some(buffer) = block.signature.get_positional(0) {
|
||||||
if let Some(buffer_id) = &buffer.var_id {
|
if let Some(buffer_id) = &buffer.var_id {
|
||||||
let line_buffer = Value::string(parsed.remainder, self.span);
|
let line_buffer = Value::String {
|
||||||
|
val: parsed.remainder.to_string(),
|
||||||
|
span: self.span,
|
||||||
|
};
|
||||||
self.stack.add_var(*buffer_id, line_buffer);
|
self.stack.add_var(*buffer_id, line_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(position) = block.signature.get_positional(1) {
|
if let Some(position) = block.signature.get_positional(1) {
|
||||||
if let Some(position_id) = &position.var_id {
|
if let Some(position_id) = &position.var_id {
|
||||||
let line_buffer = Value::int(pos as i64, self.span);
|
let line_buffer = Value::Int {
|
||||||
|
val: pos as i64,
|
||||||
|
span: self.span,
|
||||||
|
};
|
||||||
self.stack.add_var(*position_id, line_buffer);
|
self.stack.add_var(*position_id, line_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,10 +87,13 @@ fn convert_to_suggestions(
|
|||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
match value {
|
match value {
|
||||||
Value::Record { .. } => {
|
Value::Record { .. } => {
|
||||||
let text = value
|
let text = match value
|
||||||
.get_data_by_key("value")
|
.get_data_by_key("value")
|
||||||
.and_then(|val| val.as_string().ok())
|
.and_then(|val| val.as_string().ok())
|
||||||
.unwrap_or_else(|| "No value key".to_string());
|
{
|
||||||
|
Some(val) => val,
|
||||||
|
None => "No value key".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
let description = value
|
let description = value
|
||||||
.get_data_by_key("description")
|
.get_data_by_key("description")
|
||||||
@@ -154,7 +163,7 @@ fn convert_to_suggestions(
|
|||||||
.flat_map(|val| convert_to_suggestions(val, line, pos, only_buffer_difference))
|
.flat_map(|val| convert_to_suggestions(val, line, pos, only_buffer_difference))
|
||||||
.collect(),
|
.collect(),
|
||||||
_ => vec![Suggestion {
|
_ => vec![Suggestion {
|
||||||
value: format!("Not a record: {value:?}"),
|
value: format!("Not a record: {:?}", value),
|
||||||
description: None,
|
description: None,
|
||||||
extra: None,
|
extra: None,
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type, Value};
|
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Value};
|
||||||
use reedline::Highlighter;
|
use reedline::Highlighter;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -12,9 +12,7 @@ impl Command for NuHighlight {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("nu-highlight")
|
Signature::build("nu-highlight").category(Category::Strings)
|
||||||
.category(Category::Strings)
|
|
||||||
.input_output_types(vec![(Type::String, Type::String)])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@@ -35,7 +33,7 @@ impl Command for NuHighlight {
|
|||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
let engine_state = std::sync::Arc::new(engine_state.clone());
|
let engine_state = engine_state.clone();
|
||||||
let config = engine_state.get_config().clone();
|
let config = engine_state.get_config().clone();
|
||||||
|
|
||||||
let highlighter = crate::NuHighlighter {
|
let highlighter = crate::NuHighlighter {
|
||||||
@@ -53,9 +51,7 @@ impl Command for NuHighlight {
|
|||||||
span: head,
|
span: head,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => Value::Error {
|
Err(err) => Value::Error { error: err },
|
||||||
error: Box::new(err),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
ctrlc,
|
ctrlc,
|
||||||
)
|
)
|
||||||
|
@@ -2,8 +2,7 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
||||||
Value,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -16,7 +15,6 @@ impl Command for Print {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("print")
|
Signature::build("print")
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
|
||||||
.rest("rest", SyntaxShape::Any, "the values to print")
|
.rest("rest", SyntaxShape::Any, "the values to print")
|
||||||
.switch(
|
.switch(
|
||||||
"no-newline",
|
"no-newline",
|
||||||
@@ -28,7 +26,7 @@ impl Command for Print {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Print the given values to stdout."
|
"Print the given values to stdout"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
fn extra_usage(&self) -> &str {
|
||||||
@@ -47,23 +45,19 @@ Since this command has no output, there is no point in piping it with other comm
|
|||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
|
let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
|
||||||
let no_newline = call.has_flag("no-newline");
|
let no_newline = call.has_flag("no-newline");
|
||||||
let to_stderr = call.has_flag("stderr");
|
let to_stderr = call.has_flag("stderr");
|
||||||
|
let head = call.head;
|
||||||
|
|
||||||
// This will allow for easy printing of pipelines as well
|
for arg in args {
|
||||||
if !args.is_empty() {
|
arg.into_pipeline_data()
|
||||||
for arg in args {
|
.print(engine_state, stack, no_newline, to_stderr)?;
|
||||||
arg.into_pipeline_data()
|
|
||||||
.print(engine_state, stack, no_newline, to_stderr)?;
|
|
||||||
}
|
|
||||||
} else if !input.is_nothing() {
|
|
||||||
input.print(engine_state, stack, no_newline, to_stderr)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::new(head))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@@ -91,7 +91,7 @@ impl NushellPrompt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn default_wrapped_custom_string(&self, str: String) -> String {
|
fn default_wrapped_custom_string(&self, str: String) -> String {
|
||||||
format!("({str})")
|
format!("({})", str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,14 +105,12 @@ impl Prompt for NushellPrompt {
|
|||||||
if let Some(prompt_string) = &self.left_prompt_string {
|
if let Some(prompt_string) = &self.left_prompt_string {
|
||||||
prompt_string.replace('\n', "\r\n").into()
|
prompt_string.replace('\n', "\r\n").into()
|
||||||
} else {
|
} else {
|
||||||
let default = DefaultPrompt::default();
|
let default = DefaultPrompt::new();
|
||||||
let prompt = default
|
default
|
||||||
.render_prompt_left()
|
.render_prompt_left()
|
||||||
.to_string()
|
.to_string()
|
||||||
.replace('\n', "\r\n")
|
.replace('\n', "\r\n")
|
||||||
+ " ";
|
.into()
|
||||||
|
|
||||||
prompt.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +118,7 @@ impl Prompt for NushellPrompt {
|
|||||||
if let Some(prompt_string) = &self.right_prompt_string {
|
if let Some(prompt_string) = &self.right_prompt_string {
|
||||||
prompt_string.replace('\n', "\r\n").into()
|
prompt_string.replace('\n', "\r\n").into()
|
||||||
} else {
|
} else {
|
||||||
let default = DefaultPrompt::default();
|
let default = DefaultPrompt::new();
|
||||||
default
|
default
|
||||||
.render_prompt_right()
|
.render_prompt_right()
|
||||||
.to_string()
|
.to_string()
|
||||||
@@ -132,36 +130,32 @@ impl Prompt for NushellPrompt {
|
|||||||
fn render_prompt_indicator(&self, edit_mode: PromptEditMode) -> Cow<str> {
|
fn render_prompt_indicator(&self, edit_mode: PromptEditMode) -> Cow<str> {
|
||||||
match edit_mode {
|
match edit_mode {
|
||||||
PromptEditMode::Default => match &self.default_prompt_indicator {
|
PromptEditMode::Default => match &self.default_prompt_indicator {
|
||||||
Some(indicator) => indicator,
|
Some(indicator) => indicator.as_str().into(),
|
||||||
None => "> ",
|
None => "〉".into(),
|
||||||
}
|
},
|
||||||
.into(),
|
|
||||||
PromptEditMode::Emacs => match &self.default_prompt_indicator {
|
PromptEditMode::Emacs => match &self.default_prompt_indicator {
|
||||||
Some(indicator) => indicator,
|
Some(indicator) => indicator.as_str().into(),
|
||||||
None => "> ",
|
None => "〉".into(),
|
||||||
}
|
},
|
||||||
.into(),
|
|
||||||
PromptEditMode::Vi(vi_mode) => match vi_mode {
|
PromptEditMode::Vi(vi_mode) => match vi_mode {
|
||||||
PromptViMode::Normal => match &self.default_vi_normal_prompt_indicator {
|
PromptViMode::Normal => match &self.default_vi_normal_prompt_indicator {
|
||||||
Some(indicator) => indicator,
|
Some(indicator) => indicator.as_str().into(),
|
||||||
None => ": ",
|
None => ": ".into(),
|
||||||
},
|
},
|
||||||
PromptViMode::Insert => match &self.default_vi_insert_prompt_indicator {
|
PromptViMode::Insert => match &self.default_vi_insert_prompt_indicator {
|
||||||
Some(indicator) => indicator,
|
Some(indicator) => indicator.as_str().into(),
|
||||||
None => "> ",
|
None => "〉".into(),
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
.into(),
|
|
||||||
PromptEditMode::Custom(str) => self.default_wrapped_custom_string(str).into(),
|
PromptEditMode::Custom(str) => self.default_wrapped_custom_string(str).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_prompt_multiline_indicator(&self) -> Cow<str> {
|
fn render_prompt_multiline_indicator(&self) -> Cow<str> {
|
||||||
match &self.default_multiline_indicator {
|
match &self.default_multiline_indicator {
|
||||||
Some(indicator) => indicator,
|
Some(indicator) => indicator.as_str().into(),
|
||||||
None => "::: ",
|
None => "::: ".into(),
|
||||||
}
|
}
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_prompt_history_search_indicator(
|
fn render_prompt_history_search_indicator(
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
|
use crate::util::report_error;
|
||||||
use crate::NushellPrompt;
|
use crate::NushellPrompt;
|
||||||
use log::trace;
|
use log::info;
|
||||||
use nu_engine::eval_subexpression;
|
use nu_engine::eval_subexpression;
|
||||||
use nu_protocol::report_error;
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
Config, PipelineData, Value,
|
Config, PipelineData, Span, Value,
|
||||||
};
|
};
|
||||||
use reedline::Prompt;
|
use reedline::Prompt;
|
||||||
|
|
||||||
@@ -37,39 +37,52 @@ fn get_prompt_string(
|
|||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
let mut stack = stack.captures_to_stack(&captures);
|
let mut stack = stack.captures_to_stack(&captures);
|
||||||
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt
|
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt
|
||||||
let ret_val =
|
let ret_val = eval_subexpression(
|
||||||
eval_subexpression(engine_state, &mut stack, block, PipelineData::empty());
|
engine_state,
|
||||||
trace!(
|
&mut stack,
|
||||||
|
block,
|
||||||
|
PipelineData::new(Span::new(0, 0)), // Don't try this at home, 0 span is ignored
|
||||||
|
);
|
||||||
|
info!(
|
||||||
"get_prompt_string (block) {}:{}:{}",
|
"get_prompt_string (block) {}:{}:{}",
|
||||||
file!(),
|
file!(),
|
||||||
line!(),
|
line!(),
|
||||||
column!()
|
column!()
|
||||||
);
|
);
|
||||||
|
|
||||||
ret_val
|
match ret_val {
|
||||||
.map_err(|err| {
|
Ok(ret_val) => Some(ret_val),
|
||||||
|
Err(err) => {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
})
|
None
|
||||||
.ok()
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Value::Block { val: block_id, .. } => {
|
Value::Block { val: block_id, .. } => {
|
||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt
|
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt
|
||||||
let ret_val = eval_subexpression(engine_state, stack, block, PipelineData::empty());
|
let ret_val = eval_subexpression(
|
||||||
trace!(
|
engine_state,
|
||||||
|
stack,
|
||||||
|
block,
|
||||||
|
PipelineData::new(Span::new(0, 0)), // Don't try this at home, 0 span is ignored
|
||||||
|
);
|
||||||
|
info!(
|
||||||
"get_prompt_string (block) {}:{}:{}",
|
"get_prompt_string (block) {}:{}:{}",
|
||||||
file!(),
|
file!(),
|
||||||
line!(),
|
line!(),
|
||||||
column!()
|
column!()
|
||||||
);
|
);
|
||||||
|
|
||||||
ret_val
|
match ret_val {
|
||||||
.map_err(|err| {
|
Ok(ret_val) => Some(ret_val),
|
||||||
|
Err(err) => {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
})
|
None
|
||||||
.ok()
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Value::String { .. } => Some(PipelineData::Value(v.clone(), None)),
|
Value::String { .. } => Some(PipelineData::Value(v.clone(), None)),
|
||||||
_ => None,
|
_ => None,
|
||||||
@@ -77,17 +90,20 @@ fn get_prompt_string(
|
|||||||
.and_then(|pipeline_data| {
|
.and_then(|pipeline_data| {
|
||||||
let output = pipeline_data.collect_string("", config).ok();
|
let output = pipeline_data.collect_string("", config).ok();
|
||||||
|
|
||||||
output.map(|mut x| {
|
match output {
|
||||||
// Just remove the very last newline.
|
Some(mut x) => {
|
||||||
if x.ends_with('\n') {
|
// Just remove the very last newline.
|
||||||
x.pop();
|
if x.ends_with('\n') {
|
||||||
}
|
x.pop();
|
||||||
|
}
|
||||||
|
|
||||||
if x.ends_with('\r') {
|
if x.ends_with('\r') {
|
||||||
x.pop();
|
x.pop();
|
||||||
|
}
|
||||||
|
Some(x)
|
||||||
}
|
}
|
||||||
x
|
None => None,
|
||||||
})
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,12 +120,12 @@ pub(crate) fn update_prompt<'prompt>(
|
|||||||
// Now that we have the prompt string lets ansify it.
|
// Now that we have the prompt string lets ansify it.
|
||||||
// <133 A><prompt><133 B><command><133 C><command output>
|
// <133 A><prompt><133 B><command><133 C><command output>
|
||||||
let left_prompt_string = if config.shell_integration {
|
let left_prompt_string = if config.shell_integration {
|
||||||
if let Some(prompt_string) = left_prompt_string {
|
match left_prompt_string {
|
||||||
Some(format!(
|
Some(prompt_string) => Some(format!(
|
||||||
"{PRE_PROMPT_MARKER}{prompt_string}{POST_PROMPT_MARKER}"
|
"{}{}{}",
|
||||||
))
|
PRE_PROMPT_MARKER, prompt_string, POST_PROMPT_MARKER
|
||||||
} else {
|
)),
|
||||||
left_prompt_string
|
None => left_prompt_string,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
left_prompt_string
|
left_prompt_string
|
||||||
@@ -141,7 +157,7 @@ pub(crate) fn update_prompt<'prompt>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
let ret_val = nu_prompt as &dyn Prompt;
|
let ret_val = nu_prompt as &dyn Prompt;
|
||||||
trace!("update_prompt {}:{}:{}", file!(), line!(), column!());
|
info!("update_prompt {}:{}:{}", file!(), line!(), column!());
|
||||||
|
|
||||||
ret_val
|
ret_val
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
use super::DescriptionMenu;
|
use super::DescriptionMenu;
|
||||||
use crate::{menus::NuMenuCompleter, NuHelpCompleter};
|
use crate::{menus::NuMenuCompleter, NuHelpCompleter};
|
||||||
use crossterm::event::{KeyCode, KeyModifiers};
|
use crossterm::event::{KeyCode, KeyModifiers};
|
||||||
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
use nu_color_config::lookup_ansi_color_style;
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
create_menus,
|
color_value_string, create_menus,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
extract_value, Config, ParsedKeybinding, ParsedMenu, PipelineData, ShellError, Span, Value,
|
extract_value, Config, IntoPipelineData, ParsedKeybinding, ParsedMenu, PipelineData,
|
||||||
|
ShellError, Span, Value,
|
||||||
};
|
};
|
||||||
use reedline::{
|
use reedline::{
|
||||||
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
|
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
|
||||||
@@ -97,18 +98,19 @@ pub(crate) fn add_menus(
|
|||||||
{
|
{
|
||||||
let (block, _) = {
|
let (block, _) = {
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
let output = parse(
|
let (output, _) = parse(
|
||||||
&mut working_set,
|
&mut working_set,
|
||||||
Some(name), // format!("entry #{}", entry_num)
|
Some(name), // format!("entry #{}", entry_num)
|
||||||
definition.as_bytes(),
|
definition.as_bytes(),
|
||||||
true,
|
true,
|
||||||
|
&[],
|
||||||
);
|
);
|
||||||
|
|
||||||
(output, working_set.render())
|
(output, working_set.render())
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut temp_stack = Stack::new();
|
let mut temp_stack = Stack::new();
|
||||||
let input = PipelineData::Empty;
|
let input = Value::nothing(Span::test_data()).into_pipeline_data();
|
||||||
let res = eval_block(&engine_state, &mut temp_stack, &block, input, false, false)?;
|
let res = eval_block(&engine_state, &mut temp_stack, &block, input, false, false)?;
|
||||||
|
|
||||||
if let PipelineData::Value(value, None) = res {
|
if let PipelineData::Value(value, None) = res {
|
||||||
@@ -157,11 +159,14 @@ macro_rules! add_style {
|
|||||||
($name:expr, $cols: expr, $vals:expr, $span:expr, $config: expr, $menu:expr, $f:expr) => {
|
($name:expr, $cols: expr, $vals:expr, $span:expr, $config: expr, $menu:expr, $f:expr) => {
|
||||||
$menu = match extract_value($name, $cols, $vals, $span) {
|
$menu = match extract_value($name, $cols, $vals, $span) {
|
||||||
Ok(text) => {
|
Ok(text) => {
|
||||||
let style = match text {
|
let text = match text {
|
||||||
Value::String { val, .. } => lookup_ansi_color_style(&val),
|
Value::String { val, .. } => val.clone(),
|
||||||
Value::Record { .. } => color_record_to_nustyle(&text),
|
Value::Record { cols, vals, span } => {
|
||||||
_ => lookup_ansi_color_style("green"),
|
color_value_string(span, cols, vals, $config).into_string("", $config)
|
||||||
|
}
|
||||||
|
_ => "green".to_string(),
|
||||||
};
|
};
|
||||||
|
let style = lookup_ansi_color_style(&text);
|
||||||
$f($menu, style)
|
$f($menu, style)
|
||||||
}
|
}
|
||||||
Err(_) => $menu,
|
Err(_) => $menu,
|
||||||
@@ -625,12 +630,9 @@ fn add_parsed_keybinding(
|
|||||||
"shift" => KeyModifiers::SHIFT,
|
"shift" => KeyModifiers::SHIFT,
|
||||||
"alt" => KeyModifiers::ALT,
|
"alt" => KeyModifiers::ALT,
|
||||||
"none" => KeyModifiers::NONE,
|
"none" => KeyModifiers::NONE,
|
||||||
"shift_alt" | "alt_shift" => KeyModifiers::SHIFT | KeyModifiers::ALT,
|
"control | shift" => KeyModifiers::CONTROL | KeyModifiers::SHIFT,
|
||||||
"control_shift" | "shift_control" => KeyModifiers::CONTROL | KeyModifiers::SHIFT,
|
"control | alt" => KeyModifiers::CONTROL | KeyModifiers::ALT,
|
||||||
"control_alt" | "alt_control" => KeyModifiers::CONTROL | KeyModifiers::ALT,
|
"control | alt | shift" => KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SHIFT,
|
||||||
"control_alt_shift" | "control_shift_alt" => {
|
|
||||||
KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SHIFT
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
"CONTROL, SHIFT, ALT or NONE".to_string(),
|
"CONTROL, SHIFT, ALT or NONE".to_string(),
|
||||||
@@ -653,15 +655,14 @@ fn add_parsed_keybinding(
|
|||||||
let pos1 = char_iter.next();
|
let pos1 = char_iter.next();
|
||||||
let pos2 = char_iter.next();
|
let pos2 = char_iter.next();
|
||||||
|
|
||||||
let char = if let (Some(char), None) = (pos1, pos2) {
|
let char = match (pos1, pos2) {
|
||||||
char
|
(Some(char), None) => Ok(char),
|
||||||
} else {
|
_ => Err(ShellError::UnsupportedConfigValue(
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
|
||||||
"char_<CHAR: unicode codepoint>".to_string(),
|
"char_<CHAR: unicode codepoint>".to_string(),
|
||||||
c.to_string(),
|
c.to_string(),
|
||||||
keybinding.keycode.span()?,
|
keybinding.keycode.span()?,
|
||||||
));
|
)),
|
||||||
};
|
}?;
|
||||||
|
|
||||||
KeyCode::Char(char)
|
KeyCode::Char(char)
|
||||||
}
|
}
|
||||||
@@ -682,10 +683,10 @@ fn add_parsed_keybinding(
|
|||||||
let fn_num: u8 = c[1..]
|
let fn_num: u8 = c[1..]
|
||||||
.parse()
|
.parse()
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|num| matches!(num, 1..=20))
|
.filter(|num| matches!(num, 1..=12))
|
||||||
.ok_or(ShellError::UnsupportedConfigValue(
|
.ok_or(ShellError::UnsupportedConfigValue(
|
||||||
"(f1|f2|...|f20)".to_string(),
|
"(f1|f2|...|f12)".to_string(),
|
||||||
format!("unknown function key: {c}"),
|
format!("unknown function key: {}", c),
|
||||||
keybinding.keycode.span()?,
|
keybinding.keycode.span()?,
|
||||||
))?;
|
))?;
|
||||||
KeyCode::F(fn_num)
|
KeyCode::F(fn_num)
|
||||||
@@ -992,7 +993,10 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_send_event() {
|
fn test_send_event() {
|
||||||
let cols = vec!["send".to_string()];
|
let cols = vec!["send".to_string()];
|
||||||
let vals = vec![Value::test_string("Enter")];
|
let vals = vec![Value::String {
|
||||||
|
val: "Enter".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}];
|
||||||
|
|
||||||
let span = Span::test_data();
|
let span = Span::test_data();
|
||||||
let b = EventType::try_from_columns(&cols, &vals, &span).unwrap();
|
let b = EventType::try_from_columns(&cols, &vals, &span).unwrap();
|
||||||
@@ -1012,7 +1016,10 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_edit_event() {
|
fn test_edit_event() {
|
||||||
let cols = vec!["edit".to_string()];
|
let cols = vec!["edit".to_string()];
|
||||||
let vals = vec![Value::test_string("Clear")];
|
let vals = vec![Value::String {
|
||||||
|
val: "Clear".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}];
|
||||||
|
|
||||||
let span = Span::test_data();
|
let span = Span::test_data();
|
||||||
let b = EventType::try_from_columns(&cols, &vals, &span).unwrap();
|
let b = EventType::try_from_columns(&cols, &vals, &span).unwrap();
|
||||||
@@ -1036,8 +1043,14 @@ mod test {
|
|||||||
fn test_send_menu() {
|
fn test_send_menu() {
|
||||||
let cols = vec!["send".to_string(), "name".to_string()];
|
let cols = vec!["send".to_string(), "name".to_string()];
|
||||||
let vals = vec![
|
let vals = vec![
|
||||||
Value::test_string("Menu"),
|
Value::String {
|
||||||
Value::test_string("history_menu"),
|
val: "Menu".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "history_menu".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let span = Span::test_data();
|
let span = Span::test_data();
|
||||||
@@ -1063,8 +1076,14 @@ mod test {
|
|||||||
// Menu event
|
// Menu event
|
||||||
let cols = vec!["send".to_string(), "name".to_string()];
|
let cols = vec!["send".to_string(), "name".to_string()];
|
||||||
let vals = vec![
|
let vals = vec![
|
||||||
Value::test_string("Menu"),
|
Value::String {
|
||||||
Value::test_string("history_menu"),
|
val: "Menu".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "history_menu".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let menu_event = Value::Record {
|
let menu_event = Value::Record {
|
||||||
@@ -1075,7 +1094,10 @@ mod test {
|
|||||||
|
|
||||||
// Enter event
|
// Enter event
|
||||||
let cols = vec!["send".to_string()];
|
let cols = vec!["send".to_string()];
|
||||||
let vals = vec![Value::test_string("Enter")];
|
let vals = vec![Value::String {
|
||||||
|
val: "Enter".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}];
|
||||||
|
|
||||||
let enter_event = Value::Record {
|
let enter_event = Value::Record {
|
||||||
cols,
|
cols,
|
||||||
@@ -1116,8 +1138,14 @@ mod test {
|
|||||||
// Menu event
|
// Menu event
|
||||||
let cols = vec!["send".to_string(), "name".to_string()];
|
let cols = vec!["send".to_string(), "name".to_string()];
|
||||||
let vals = vec![
|
let vals = vec![
|
||||||
Value::test_string("Menu"),
|
Value::String {
|
||||||
Value::test_string("history_menu"),
|
val: "Menu".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "history_menu".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let menu_event = Value::Record {
|
let menu_event = Value::Record {
|
||||||
@@ -1128,7 +1156,10 @@ mod test {
|
|||||||
|
|
||||||
// Enter event
|
// Enter event
|
||||||
let cols = vec!["send".to_string()];
|
let cols = vec!["send".to_string()];
|
||||||
let vals = vec![Value::test_string("Enter")];
|
let vals = vec![Value::String {
|
||||||
|
val: "Enter".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}];
|
||||||
|
|
||||||
let enter_event = Value::Record {
|
let enter_event = Value::Record {
|
||||||
cols,
|
cols,
|
||||||
@@ -1156,7 +1187,10 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_error() {
|
fn test_error() {
|
||||||
let cols = vec!["not_exist".to_string()];
|
let cols = vec!["not_exist".to_string()];
|
||||||
let vals = vec![Value::test_string("Enter")];
|
let vals = vec![Value::String {
|
||||||
|
val: "Enter".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}];
|
||||||
|
|
||||||
let span = Span::test_data();
|
let span = Span::test_data();
|
||||||
let b = EventType::try_from_columns(&cols, &vals, &span);
|
let b = EventType::try_from_columns(&cols, &vals, &span);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -6,10 +6,9 @@ use nu_protocol::ast::{Argument, Block, Expr, Expression, PipelineElement};
|
|||||||
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
||||||
use nu_protocol::{Config, Span};
|
use nu_protocol::{Config, Span};
|
||||||
use reedline::{Highlighter, StyledText};
|
use reedline::{Highlighter, StyledText};
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub struct NuHighlighter {
|
pub struct NuHighlighter {
|
||||||
pub engine_state: Arc<EngineState>,
|
pub engine_state: EngineState,
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,7 +17,10 @@ impl Highlighter for NuHighlighter {
|
|||||||
trace!("highlighting: {}", line);
|
trace!("highlighting: {}", line);
|
||||||
|
|
||||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
let block = parse(&mut working_set, None, line.as_bytes(), false);
|
let block = {
|
||||||
|
let (block, _) = parse(&mut working_set, None, line.as_bytes(), false, &[]);
|
||||||
|
block
|
||||||
|
};
|
||||||
let (shapes, global_span_offset) = {
|
let (shapes, global_span_offset) = {
|
||||||
let shapes = flatten_block(&working_set, &block);
|
let shapes = flatten_block(&working_set, &block);
|
||||||
(shapes, self.engine_state.next_span_start())
|
(shapes, self.engine_state.next_span_start())
|
||||||
@@ -76,28 +78,29 @@ impl Highlighter for NuHighlighter {
|
|||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut add_colored_token = |shape: &FlatShape, text: String| {
|
macro_rules! add_colored_token {
|
||||||
output.push((get_shape_color(shape.to_string(), &self.config), text));
|
($shape:expr, $text:expr) => {
|
||||||
};
|
output.push((get_shape_color($shape.to_string(), &self.config), $text))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
match shape.1 {
|
match shape.1 {
|
||||||
FlatShape::Garbage => add_colored_token(&shape.1, next_token),
|
FlatShape::Garbage => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Nothing => add_colored_token(&shape.1, next_token),
|
FlatShape::Nothing => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Binary => add_colored_token(&shape.1, next_token),
|
FlatShape::Binary => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Bool => add_colored_token(&shape.1, next_token),
|
FlatShape::Bool => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Int => add_colored_token(&shape.1, next_token),
|
FlatShape::Int => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Float => add_colored_token(&shape.1, next_token),
|
FlatShape::Float => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Range => add_colored_token(&shape.1, next_token),
|
FlatShape::Range => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::InternalCall(_) => add_colored_token(&shape.1, next_token),
|
FlatShape::InternalCall => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::External => add_colored_token(&shape.1, next_token),
|
FlatShape::External => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::ExternalArg => add_colored_token(&shape.1, next_token),
|
FlatShape::ExternalArg => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Keyword => add_colored_token(&shape.1, next_token),
|
FlatShape::Literal => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Literal => add_colored_token(&shape.1, next_token),
|
FlatShape::Operator => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Operator => add_colored_token(&shape.1, next_token),
|
FlatShape::Signature => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Signature => add_colored_token(&shape.1, next_token),
|
FlatShape::String => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::String => add_colored_token(&shape.1, next_token),
|
FlatShape::StringInterpolation => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::StringInterpolation => add_colored_token(&shape.1, next_token),
|
FlatShape::DateTime => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::DateTime => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::List => {
|
FlatShape::List => {
|
||||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||||
}
|
}
|
||||||
@@ -111,23 +114,17 @@ impl Highlighter for NuHighlighter {
|
|||||||
FlatShape::Block => {
|
FlatShape::Block => {
|
||||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||||
}
|
}
|
||||||
FlatShape::Closure => {
|
|
||||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
|
||||||
}
|
|
||||||
|
|
||||||
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
|
FlatShape::Filepath => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Directory => add_colored_token(&shape.1, next_token),
|
FlatShape::Directory => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::GlobPattern => add_colored_token(&shape.1, next_token),
|
FlatShape::GlobPattern => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Variable(_) | FlatShape::VarDecl(_) => {
|
FlatShape::Variable => add_colored_token!(shape.1, next_token),
|
||||||
add_colored_token(&shape.1, next_token)
|
FlatShape::Flag => add_colored_token!(shape.1, next_token),
|
||||||
}
|
FlatShape::Pipe => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Flag => add_colored_token(&shape.1, next_token),
|
FlatShape::And => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Pipe => add_colored_token(&shape.1, next_token),
|
FlatShape::Or => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::And => add_colored_token(&shape.1, next_token),
|
FlatShape::Redirection => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Or => add_colored_token(&shape.1, next_token),
|
FlatShape::Custom(..) => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Redirection => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::Custom(..) => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::MatchPattern => add_colored_token(&shape.1, next_token),
|
|
||||||
}
|
}
|
||||||
last_seen_span = shape.0.end;
|
last_seen_span = shape.0.end;
|
||||||
}
|
}
|
||||||
@@ -152,7 +149,7 @@ fn split_span_by_highlight_positions(
|
|||||||
for pos in highlight_positions {
|
for pos in highlight_positions {
|
||||||
if start <= *pos && pos < &span.end {
|
if start <= *pos && pos < &span.end {
|
||||||
if start < *pos {
|
if start < *pos {
|
||||||
result.push((Span::new(start, *pos), false));
|
result.push((Span { start, end: *pos }, false));
|
||||||
}
|
}
|
||||||
let span_str = &line[pos - global_span_offset..span.end - global_span_offset];
|
let span_str = &line[pos - global_span_offset..span.end - global_span_offset];
|
||||||
let end = span_str
|
let end = span_str
|
||||||
@@ -160,12 +157,18 @@ fn split_span_by_highlight_positions(
|
|||||||
.next()
|
.next()
|
||||||
.map(|c| pos + get_char_length(c))
|
.map(|c| pos + get_char_length(c))
|
||||||
.unwrap_or(pos + 1);
|
.unwrap_or(pos + 1);
|
||||||
result.push((Span::new(*pos, end), true));
|
result.push((Span { start: *pos, end }, true));
|
||||||
start = end;
|
start = end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if start < span.end {
|
if start < span.end {
|
||||||
result.push((Span::new(start, span.end), false));
|
result.push((
|
||||||
|
Span {
|
||||||
|
start,
|
||||||
|
end: span.end,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@@ -236,9 +239,7 @@ fn find_matching_block_end_in_block(
|
|||||||
PipelineElement::Expression(_, e)
|
PipelineElement::Expression(_, e)
|
||||||
| PipelineElement::Redirection(_, _, e)
|
| PipelineElement::Redirection(_, _, e)
|
||||||
| PipelineElement::And(_, e)
|
| PipelineElement::And(_, e)
|
||||||
| PipelineElement::Or(_, e)
|
| PipelineElement::Or(_, e) => {
|
||||||
| PipelineElement::SameTargetRedirection { cmd: (_, e), .. }
|
|
||||||
| PipelineElement::SeparateRedirection { out: (_, e), .. } => {
|
|
||||||
if e.span.contains(global_cursor_offset) {
|
if e.span.contains(global_cursor_offset) {
|
||||||
if let Some(pos) = find_matching_block_end_in_expr(
|
if let Some(pos) = find_matching_block_end_in_expr(
|
||||||
line,
|
line,
|
||||||
@@ -311,8 +312,6 @@ fn find_matching_block_end_in_expr(
|
|||||||
Expr::ImportPattern(_) => None,
|
Expr::ImportPattern(_) => None,
|
||||||
Expr::Overlay(_) => None,
|
Expr::Overlay(_) => None,
|
||||||
Expr::Signature(_) => None,
|
Expr::Signature(_) => None,
|
||||||
Expr::MatchPattern(_) => None,
|
|
||||||
Expr::MatchBlock(_) => None,
|
|
||||||
Expr::Nothing => None,
|
Expr::Nothing => None,
|
||||||
Expr::Garbage => None,
|
Expr::Garbage => None,
|
||||||
|
|
||||||
@@ -359,7 +358,6 @@ fn find_matching_block_end_in_expr(
|
|||||||
let opt_expr = match arg {
|
let opt_expr = match arg {
|
||||||
Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(),
|
Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(),
|
||||||
Argument::Positional(inner_expr) => Some(inner_expr),
|
Argument::Positional(inner_expr) => Some(inner_expr),
|
||||||
Argument::Unknown(inner_expr) => Some(inner_expr),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(inner_expr) = opt_expr {
|
if let Some(inner_expr) = opt_expr {
|
||||||
|
@@ -1,16 +1,15 @@
|
|||||||
use nu_command::hook::eval_hook;
|
use crate::repl::eval_hook;
|
||||||
use nu_engine::{eval_block, eval_block_with_early_return};
|
use nu_engine::eval_block;
|
||||||
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
|
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
|
||||||
use nu_protocol::engine::StateWorkingSet;
|
use nu_protocol::engine::StateWorkingSet;
|
||||||
|
use nu_protocol::CliError;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
print_if_stream, PipelineData, ShellError, Span, Value,
|
print_if_stream, PipelineData, ShellError, Span, Value,
|
||||||
};
|
};
|
||||||
use nu_protocol::{report_error, report_error_new};
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use nu_utils::enable_vt_processing;
|
use nu_utils::enable_vt_processing;
|
||||||
use nu_utils::utils::perf;
|
use std::path::{Path, PathBuf};
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
// This will collect environment variables from std::env and adds them to a stack.
|
// This will collect environment variables from std::env and adds them to a stack.
|
||||||
//
|
//
|
||||||
@@ -44,7 +43,7 @@ fn gather_env_vars(
|
|||||||
report_error(
|
report_error(
|
||||||
&working_set,
|
&working_set,
|
||||||
&ShellError::GenericError(
|
&ShellError::GenericError(
|
||||||
format!("Environment variable was not captured: {env_str}"),
|
format!("Environment variable was not captured: {}", env_str),
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
None,
|
None,
|
||||||
Some(msg.into()),
|
Some(msg.into()),
|
||||||
@@ -80,7 +79,8 @@ fn gather_env_vars(
|
|||||||
"".to_string(),
|
"".to_string(),
|
||||||
None,
|
None,
|
||||||
Some(format!(
|
Some(format!(
|
||||||
"Retrieving current directory failed: {init_cwd:?} not a valid utf-8 path"
|
"Retrieving current directory failed: {:?} not a valid utf-8 path",
|
||||||
|
init_cwd
|
||||||
)),
|
)),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
),
|
),
|
||||||
@@ -113,8 +113,7 @@ fn gather_env_vars(
|
|||||||
span,
|
span,
|
||||||
}) = parts.get(0)
|
}) = parts.get(0)
|
||||||
{
|
{
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let bytes = engine_state.get_span_contents(span);
|
||||||
let bytes = working_set.get_span_contents(*span);
|
|
||||||
|
|
||||||
if bytes.len() < 2 {
|
if bytes.len() < 2 {
|
||||||
report_capture_error(
|
report_capture_error(
|
||||||
@@ -126,12 +125,9 @@ fn gather_env_vars(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (bytes, err) = unescape_unquote_string(bytes, *span);
|
let (bytes, parse_error) = unescape_unquote_string(bytes, *span);
|
||||||
if let Some(err) = err {
|
|
||||||
working_set.error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if working_set.parse_errors.first().is_some() {
|
if parse_error.is_some() {
|
||||||
report_capture_error(
|
report_capture_error(
|
||||||
engine_state,
|
engine_state,
|
||||||
&String::from_utf8_lossy(contents),
|
&String::from_utf8_lossy(contents),
|
||||||
@@ -157,8 +153,7 @@ fn gather_env_vars(
|
|||||||
span,
|
span,
|
||||||
}) = parts.get(2)
|
}) = parts.get(2)
|
||||||
{
|
{
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let bytes = engine_state.get_span_contents(span);
|
||||||
let bytes = working_set.get_span_contents(*span);
|
|
||||||
|
|
||||||
if bytes.len() < 2 {
|
if bytes.len() < 2 {
|
||||||
report_capture_error(
|
report_capture_error(
|
||||||
@@ -170,12 +165,9 @@ fn gather_env_vars(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (bytes, err) = unescape_unquote_string(bytes, *span);
|
let (bytes, parse_error) = unescape_unquote_string(bytes, *span);
|
||||||
if let Some(err) = err {
|
|
||||||
working_set.error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if working_set.parse_errors.first().is_some() {
|
if parse_error.is_some() {
|
||||||
report_capture_error(
|
report_capture_error(
|
||||||
engine_state,
|
engine_state,
|
||||||
&String::from_utf8_lossy(contents),
|
&String::from_utf8_lossy(contents),
|
||||||
@@ -211,21 +203,19 @@ pub fn eval_source(
|
|||||||
source: &[u8],
|
source: &[u8],
|
||||||
fname: &str,
|
fname: &str,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
allow_return: bool,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let start_time = std::time::Instant::now();
|
|
||||||
|
|
||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
let output = parse(
|
let (output, err) = parse(
|
||||||
&mut working_set,
|
&mut working_set,
|
||||||
Some(fname), // format!("entry #{}", entry_num)
|
Some(fname), // format!("entry #{}", entry_num)
|
||||||
source,
|
source,
|
||||||
false,
|
false,
|
||||||
|
&[],
|
||||||
);
|
);
|
||||||
if let Some(err) = working_set.parse_errors.first() {
|
if let Some(err) = err {
|
||||||
set_last_exit_code(stack, 1);
|
set_last_exit_code(stack, 1);
|
||||||
report_error(&working_set, err);
|
report_error(&working_set, &err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,13 +228,7 @@ pub fn eval_source(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let b = if allow_return {
|
match eval_block(engine_state, stack, &block, input, false, false) {
|
||||||
eval_block_with_early_return(engine_state, stack, &block, input, false, false)
|
|
||||||
} else {
|
|
||||||
eval_block(engine_state, stack, &block, input, false, false)
|
|
||||||
};
|
|
||||||
|
|
||||||
match b {
|
|
||||||
Ok(pipeline_data) => {
|
Ok(pipeline_data) => {
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
let result;
|
let result;
|
||||||
@@ -266,7 +250,7 @@ pub fn eval_source(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result = pipeline_data.print(engine_state, stack, true, false);
|
result = pipeline_data.print(engine_state, stack, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
@@ -298,14 +282,6 @@ pub fn eval_source(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
perf(
|
|
||||||
&format!("eval_source {}", &fname),
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
engine_state.get_config().use_ansi_coloring,
|
|
||||||
);
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -313,10 +289,58 @@ pub fn eval_source(
|
|||||||
fn set_last_exit_code(stack: &mut Stack, exit_code: i64) {
|
fn set_last_exit_code(stack: &mut Stack, exit_code: i64) {
|
||||||
stack.add_env_var(
|
stack.add_env_var(
|
||||||
"LAST_EXIT_CODE".to_string(),
|
"LAST_EXIT_CODE".to_string(),
|
||||||
Value::int(exit_code, Span::unknown()),
|
Value::Int {
|
||||||
|
val: exit_code,
|
||||||
|
span: Span { start: 0, end: 0 },
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn report_error(
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
error: &(dyn miette::Diagnostic + Send + Sync + 'static),
|
||||||
|
) {
|
||||||
|
eprintln!("Error: {:?}", CliError(error, working_set));
|
||||||
|
// reset vt processing, aka ansi because illbehaved externals can break it
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let _ = nu_utils::enable_vt_processing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn report_error_new(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
error: &(dyn miette::Diagnostic + Send + Sync + 'static),
|
||||||
|
) {
|
||||||
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
|
report_error(&working_set, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_init_cwd() -> PathBuf {
|
||||||
|
match std::env::current_dir() {
|
||||||
|
Ok(cwd) => cwd,
|
||||||
|
Err(_) => match std::env::var("PWD") {
|
||||||
|
Ok(cwd) => PathBuf::from(cwd),
|
||||||
|
Err(_) => match nu_path::home_dir() {
|
||||||
|
Some(cwd) => cwd,
|
||||||
|
None => PathBuf::new(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
|
||||||
|
match nu_engine::env::current_dir(engine_state, stack) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => {
|
||||||
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
report_error(&working_set, &e);
|
||||||
|
get_init_cwd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@@ -1,24 +1,17 @@
|
|||||||
use nu_parser::parse;
|
use nu_parser::{parse, ParseError};
|
||||||
use nu_protocol::{
|
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
||||||
engine::{EngineState, StateWorkingSet},
|
|
||||||
ParseError,
|
|
||||||
};
|
|
||||||
use reedline::{ValidationResult, Validator};
|
use reedline::{ValidationResult, Validator};
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub struct NuValidator {
|
pub struct NuValidator {
|
||||||
pub engine_state: Arc<EngineState>,
|
pub engine_state: EngineState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Validator for NuValidator {
|
impl Validator for NuValidator {
|
||||||
fn validate(&self, line: &str) -> ValidationResult {
|
fn validate(&self, line: &str) -> ValidationResult {
|
||||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
parse(&mut working_set, None, line.as_bytes(), false);
|
let (_, err) = parse(&mut working_set, None, line.as_bytes(), false, &[]);
|
||||||
|
|
||||||
if matches!(
|
if matches!(err, Some(ParseError::UnexpectedEof(..))) {
|
||||||
working_set.parse_errors.first(),
|
|
||||||
Some(ParseError::UnexpectedEof(..))
|
|
||||||
) {
|
|
||||||
ValidationResult::Incomplete
|
ValidationResult::Incomplete
|
||||||
} else {
|
} else {
|
||||||
ValidationResult::Complete
|
ValidationResult::Complete
|
||||||
|
@@ -5,7 +5,7 @@ use nu_parser::parse;
|
|||||||
use nu_protocol::engine::StateWorkingSet;
|
use nu_protocol::engine::StateWorkingSet;
|
||||||
use reedline::{Completer, Suggestion};
|
use reedline::{Completer, Suggestion};
|
||||||
use rstest::{fixture, rstest};
|
use rstest::{fixture, rstest};
|
||||||
use support::{completions_helpers::new_quote_engine, file, folder, match_suggestions, new_engine};
|
use support::{file, folder, match_suggestions, new_engine};
|
||||||
|
|
||||||
#[fixture]
|
#[fixture]
|
||||||
fn completer() -> NuCompleter {
|
fn completer() -> NuCompleter {
|
||||||
@@ -34,26 +34,6 @@ fn completer_strings() -> NuCompleter {
|
|||||||
NuCompleter::new(std::sync::Arc::new(engine), stack)
|
NuCompleter::new(std::sync::Arc::new(engine), stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[fixture]
|
|
||||||
fn extern_completer() -> NuCompleter {
|
|
||||||
// Create a new engine
|
|
||||||
let (dir, _, mut engine, mut stack) = new_engine();
|
|
||||||
|
|
||||||
// Add record value as example
|
|
||||||
let record = r#"
|
|
||||||
def animals [] { [ "cat", "dog", "eel" ] }
|
|
||||||
extern spam [
|
|
||||||
animal: string@animals
|
|
||||||
--foo (-f): string@animals
|
|
||||||
-b: string@animals
|
|
||||||
]
|
|
||||||
"#;
|
|
||||||
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
|
||||||
|
|
||||||
// Instantiate a new completer
|
|
||||||
NuCompleter::new(std::sync::Arc::new(engine), stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn variables_dollar_sign_with_varialblecompletion() {
|
fn variables_dollar_sign_with_varialblecompletion() {
|
||||||
let (_, _, engine, stack) = new_engine();
|
let (_, _, engine, stack) = new_engine();
|
||||||
@@ -109,7 +89,7 @@ fn dotnu_completions() {
|
|||||||
// Create a new engine
|
// Create a new engine
|
||||||
let (_, _, engine, stack) = new_engine();
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
// Instantiate a new completer
|
// Instatiate a new completer
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
// Test source completion
|
// Test source completion
|
||||||
@@ -169,20 +149,20 @@ fn file_completions() {
|
|||||||
// Create a new engine
|
// Create a new engine
|
||||||
let (dir, dir_str, engine, stack) = new_engine();
|
let (dir, dir_str, engine, stack) = new_engine();
|
||||||
|
|
||||||
// Instantiate a new completer
|
// Instatiate a new completer
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
// Test completions for the current folder
|
// Test completions for the current folder
|
||||||
let target_dir = format!("cp {dir_str}");
|
let target_dir = format!("cp {}", dir_str);
|
||||||
let suggestions = completer.complete(&target_dir, target_dir.len());
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
// Create the expected values
|
// Create the expected values
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
folder(dir.join("another")),
|
|
||||||
file(dir.join("custom_completion.nu")),
|
|
||||||
file(dir.join("nushell")),
|
file(dir.join("nushell")),
|
||||||
folder(dir.join("test_a")),
|
folder(dir.join("test_a")),
|
||||||
folder(dir.join("test_b")),
|
folder(dir.join("test_b")),
|
||||||
|
folder(dir.join("another")),
|
||||||
|
file(dir.join("custom_completion.nu")),
|
||||||
file(dir.join(".hidden_file")),
|
file(dir.join(".hidden_file")),
|
||||||
folder(dir.join(".hidden_folder")),
|
folder(dir.join(".hidden_folder")),
|
||||||
];
|
];
|
||||||
@@ -212,21 +192,21 @@ fn command_ls_with_filecompletion() {
|
|||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a\\".to_string(),
|
"test_a\\".to_string(),
|
||||||
"test_b\\".to_string(),
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder\\".to_string(),
|
".hidden_folder\\".to_string(),
|
||||||
];
|
];
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a/".to_string(),
|
"test_a/".to_string(),
|
||||||
"test_b/".to_string(),
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
@@ -244,21 +224,21 @@ fn command_open_with_filecompletion() {
|
|||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a\\".to_string(),
|
"test_a\\".to_string(),
|
||||||
"test_b\\".to_string(),
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder\\".to_string(),
|
".hidden_folder\\".to_string(),
|
||||||
];
|
];
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a/".to_string(),
|
"test_a/".to_string(),
|
||||||
"test_b/".to_string(),
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
@@ -277,21 +257,21 @@ fn command_rm_with_globcompletion() {
|
|||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a\\".to_string(),
|
"test_a\\".to_string(),
|
||||||
"test_b\\".to_string(),
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder\\".to_string(),
|
".hidden_folder\\".to_string(),
|
||||||
];
|
];
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a/".to_string(),
|
"test_a/".to_string(),
|
||||||
"test_b/".to_string(),
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
@@ -310,21 +290,21 @@ fn command_cp_with_globcompletion() {
|
|||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a\\".to_string(),
|
"test_a\\".to_string(),
|
||||||
"test_b\\".to_string(),
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder\\".to_string(),
|
".hidden_folder\\".to_string(),
|
||||||
];
|
];
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a/".to_string(),
|
"test_a/".to_string(),
|
||||||
"test_b/".to_string(),
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
@@ -343,21 +323,21 @@ fn command_save_with_filecompletion() {
|
|||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a\\".to_string(),
|
"test_a\\".to_string(),
|
||||||
"test_b\\".to_string(),
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder\\".to_string(),
|
".hidden_folder\\".to_string(),
|
||||||
];
|
];
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a/".to_string(),
|
"test_a/".to_string(),
|
||||||
"test_b/".to_string(),
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
@@ -376,21 +356,21 @@ fn command_touch_with_filecompletion() {
|
|||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a\\".to_string(),
|
"test_a\\".to_string(),
|
||||||
"test_b\\".to_string(),
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder\\".to_string(),
|
".hidden_folder\\".to_string(),
|
||||||
];
|
];
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a/".to_string(),
|
"test_a/".to_string(),
|
||||||
"test_b/".to_string(),
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
@@ -409,21 +389,21 @@ fn command_watch_with_filecompletion() {
|
|||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a\\".to_string(),
|
"test_a\\".to_string(),
|
||||||
"test_b\\".to_string(),
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder\\".to_string(),
|
".hidden_folder\\".to_string(),
|
||||||
];
|
];
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a/".to_string(),
|
"test_a/".to_string(),
|
||||||
"test_b/".to_string(),
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
@@ -431,36 +411,17 @@ fn command_watch_with_filecompletion() {
|
|||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(expected_paths, suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn file_completion_quoted() {
|
|
||||||
let (_, _, engine, stack) = new_quote_engine();
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
let target_dir = "open ";
|
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
||||||
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"`te st.txt`".to_string(),
|
|
||||||
"`te#st.txt`".to_string(),
|
|
||||||
"`te'st.txt`".to_string(),
|
|
||||||
"`te(st).txt`".to_string(),
|
|
||||||
];
|
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn flag_completions() {
|
fn flag_completions() {
|
||||||
// Create a new engine
|
// Create a new engine
|
||||||
let (_, _, engine, stack) = new_engine();
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
// Instantiate a new completer
|
// Instatiate a new completer
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
// Test completions for the 'ls' flags
|
// Test completions for the 'ls' flags
|
||||||
let suggestions = completer.complete("ls -", 4);
|
let suggestions = completer.complete("ls -", 4);
|
||||||
|
|
||||||
assert_eq!(16, suggestions.len());
|
assert_eq!(14, suggestions.len());
|
||||||
|
|
||||||
let expected: Vec<String> = vec![
|
let expected: Vec<String> = vec![
|
||||||
"--all".into(),
|
"--all".into(),
|
||||||
@@ -469,7 +430,6 @@ fn flag_completions() {
|
|||||||
"--full-paths".into(),
|
"--full-paths".into(),
|
||||||
"--help".into(),
|
"--help".into(),
|
||||||
"--long".into(),
|
"--long".into(),
|
||||||
"--mime-type".into(),
|
|
||||||
"--short-names".into(),
|
"--short-names".into(),
|
||||||
"-D".into(),
|
"-D".into(),
|
||||||
"-a".into(),
|
"-a".into(),
|
||||||
@@ -477,7 +437,6 @@ fn flag_completions() {
|
|||||||
"-f".into(),
|
"-f".into(),
|
||||||
"-h".into(),
|
"-h".into(),
|
||||||
"-l".into(),
|
"-l".into(),
|
||||||
"-m".into(),
|
|
||||||
"-s".into(),
|
"-s".into(),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -490,18 +449,18 @@ fn folder_with_directorycompletions() {
|
|||||||
// Create a new engine
|
// Create a new engine
|
||||||
let (dir, dir_str, engine, stack) = new_engine();
|
let (dir, dir_str, engine, stack) = new_engine();
|
||||||
|
|
||||||
// Instantiate a new completer
|
// Instatiate a new completer
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
// Test completions for the current folder
|
// Test completions for the current folder
|
||||||
let target_dir = format!("cd {dir_str}");
|
let target_dir = format!("cd {}", dir_str);
|
||||||
let suggestions = completer.complete(&target_dir, target_dir.len());
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
// Create the expected values
|
// Create the expected values
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
folder(dir.join("another")),
|
|
||||||
folder(dir.join("test_a")),
|
folder(dir.join("test_a")),
|
||||||
folder(dir.join("test_b")),
|
folder(dir.join("test_b")),
|
||||||
|
folder(dir.join("another")),
|
||||||
folder(dir.join(".hidden_folder")),
|
folder(dir.join(".hidden_folder")),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -518,28 +477,23 @@ fn variables_completions() {
|
|||||||
let record = "let actor = { name: 'Tom Hardy', age: 44 }";
|
let record = "let actor = { name: 'Tom Hardy', age: 44 }";
|
||||||
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
// Instantiate a new completer
|
// Instatiate a new completer
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
// Test completions for $nu
|
// Test completions for $nu
|
||||||
let suggestions = completer.complete("$nu.", 4);
|
let suggestions = completer.complete("$nu.", 4);
|
||||||
|
|
||||||
assert_eq!(14, suggestions.len());
|
assert_eq!(9, suggestions.len());
|
||||||
|
|
||||||
let expected: Vec<String> = vec![
|
let expected: Vec<String> = vec![
|
||||||
"config-path".into(),
|
"config-path".into(),
|
||||||
"current-exe".into(),
|
|
||||||
"default-config-dir".into(),
|
|
||||||
"env-path".into(),
|
"env-path".into(),
|
||||||
"history-path".into(),
|
"history-path".into(),
|
||||||
"home-path".into(),
|
"home-path".into(),
|
||||||
"is-interactive".into(),
|
|
||||||
"is-login".into(),
|
|
||||||
"loginshell-path".into(),
|
"loginshell-path".into(),
|
||||||
"os-info".into(),
|
"os-info".into(),
|
||||||
"pid".into(),
|
"pid".into(),
|
||||||
"plugin-path".into(),
|
"scope".into(),
|
||||||
"startup-time".into(),
|
|
||||||
"temp-path".into(),
|
"temp-path".into(),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -556,18 +510,6 @@ fn variables_completions() {
|
|||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(expected, suggestions);
|
||||||
|
|
||||||
// Test completions for $nu.os-info
|
|
||||||
let suggestions = completer.complete("$nu.os-info.", 12);
|
|
||||||
assert_eq!(4, suggestions.len());
|
|
||||||
let expected: Vec<String> = vec![
|
|
||||||
"arch".into(),
|
|
||||||
"family".into(),
|
|
||||||
"kernel_version".into(),
|
|
||||||
"name".into(),
|
|
||||||
];
|
|
||||||
// Match results
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
|
|
||||||
// Test completions for custom var
|
// Test completions for custom var
|
||||||
let suggestions = completer.complete("$actor.", 7);
|
let suggestions = completer.complete("$actor.", 7);
|
||||||
|
|
||||||
@@ -591,12 +533,9 @@ fn variables_completions() {
|
|||||||
// Test completions for $env
|
// Test completions for $env
|
||||||
let suggestions = completer.complete("$env.", 5);
|
let suggestions = completer.complete("$env.", 5);
|
||||||
|
|
||||||
assert_eq!(3, suggestions.len());
|
assert_eq!(2, suggestions.len());
|
||||||
|
|
||||||
#[cfg(windows)]
|
let expected: Vec<String> = vec!["PWD".into(), "TEST".into()];
|
||||||
let expected: Vec<String> = vec!["PWD".into(), "Path".into(), "TEST".into()];
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let expected: Vec<String> = vec!["PATH".into(), "PWD".into(), "TEST".into()];
|
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(expected, suggestions);
|
||||||
@@ -677,8 +616,8 @@ fn run_external_completion(block: &str, input: &str) -> Vec<Suggestion> {
|
|||||||
let (dir, _, mut engine_state, mut stack) = new_engine();
|
let (dir, _, mut engine_state, mut stack) = new_engine();
|
||||||
let (_, delta) = {
|
let (_, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
let block = parse(&mut working_set, None, block.as_bytes(), false);
|
let (block, err) = parse(&mut working_set, None, block.as_bytes(), false, &[]);
|
||||||
assert!(working_set.parse_errors.is_empty());
|
assert!(err.is_none());
|
||||||
|
|
||||||
(block, working_set.render())
|
(block, working_set.render())
|
||||||
};
|
};
|
||||||
@@ -695,7 +634,7 @@ fn run_external_completion(block: &str, input: &str) -> Vec<Suggestion> {
|
|||||||
config.external_completer = Some(latest_block_id);
|
config.external_completer = Some(latest_block_id);
|
||||||
engine_state.set_config(&config);
|
engine_state.set_config(&config);
|
||||||
|
|
||||||
// Instantiate a new completer
|
// Instatiate a new completer
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine_state), stack);
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine_state), stack);
|
||||||
|
|
||||||
completer.complete(input, input.len())
|
completer.complete(input, input.len())
|
||||||
@@ -712,21 +651,21 @@ fn unknown_command_completion() {
|
|||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a\\".to_string(),
|
"test_a\\".to_string(),
|
||||||
"test_b\\".to_string(),
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder\\".to_string(),
|
".hidden_folder\\".to_string(),
|
||||||
];
|
];
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a/".to_string(),
|
"test_a/".to_string(),
|
||||||
"test_b/".to_string(),
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
@@ -772,113 +711,24 @@ fn filecompletions_triggers_after_cursor() {
|
|||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a\\".to_string(),
|
"test_a\\".to_string(),
|
||||||
"test_b\\".to_string(),
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder\\".to_string(),
|
".hidden_folder\\".to_string(),
|
||||||
];
|
];
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
"nushell".to_string(),
|
"nushell".to_string(),
|
||||||
"test_a/".to_string(),
|
"test_a/".to_string(),
|
||||||
"test_b/".to_string(),
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
".hidden_file".to_string(),
|
".hidden_file".to_string(),
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(expected_paths, suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn extern_custom_completion_positional(mut extern_completer: NuCompleter) {
|
|
||||||
let suggestions = extern_completer.complete("spam ", 5);
|
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn extern_custom_completion_long_flag_1(mut extern_completer: NuCompleter) {
|
|
||||||
let suggestions = extern_completer.complete("spam --foo=", 11);
|
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn extern_custom_completion_long_flag_2(mut extern_completer: NuCompleter) {
|
|
||||||
let suggestions = extern_completer.complete("spam --foo ", 11);
|
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn extern_custom_completion_long_flag_short(mut extern_completer: NuCompleter) {
|
|
||||||
let suggestions = extern_completer.complete("spam -f ", 8);
|
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn extern_custom_completion_short_flag(mut extern_completer: NuCompleter) {
|
|
||||||
let suggestions = extern_completer.complete("spam -b ", 8);
|
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn extern_complete_flags(mut extern_completer: NuCompleter) {
|
|
||||||
let suggestions = extern_completer.complete("spam -", 6);
|
|
||||||
let expected: Vec<String> = vec!["--foo".into(), "-b".into(), "-f".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[ignore = "was reverted, still needs fixing"]
|
|
||||||
#[rstest]
|
|
||||||
fn alias_offset_bug_7648() {
|
|
||||||
let (dir, _, mut engine, mut stack) = new_engine();
|
|
||||||
|
|
||||||
// Create an alias
|
|
||||||
let alias = r#"alias ea = ^$env.EDITOR /tmp/test.s"#;
|
|
||||||
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
// Issue #7648
|
|
||||||
// Nushell crashes when an alias name is shorter than the alias command
|
|
||||||
// and the alias command is a external command
|
|
||||||
// This happens because of offset is not correct.
|
|
||||||
// This crashes before PR #7779
|
|
||||||
let _suggestions = completer.complete("e", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[ignore = "was reverted, still needs fixing"]
|
|
||||||
#[rstest]
|
|
||||||
fn alias_offset_bug_7754() {
|
|
||||||
let (dir, _, mut engine, mut stack) = new_engine();
|
|
||||||
|
|
||||||
// Create an alias
|
|
||||||
let alias = r#"alias ll = ls -l"#;
|
|
||||||
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
// Issue #7754
|
|
||||||
// Nushell crashes when an alias name is shorter than the alias command
|
|
||||||
// and the alias command contains pipes.
|
|
||||||
// This crashes before PR #7756
|
|
||||||
let _suggestions = completer.complete("ll -a | c", 9);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_path_env_var_8003() {
|
|
||||||
// Create a new engine
|
|
||||||
let (_, _, engine, _) = new_engine();
|
|
||||||
// Get the path env var in a platform agnostic way
|
|
||||||
let the_path = engine.get_path_env_var();
|
|
||||||
// Make sure it's not empty
|
|
||||||
assert!(the_path.is_some());
|
|
||||||
}
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use nu_command::create_default_context;
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
@@ -10,10 +11,6 @@ use nu_test_support::fs;
|
|||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
const SEP: char = std::path::MAIN_SEPARATOR;
|
const SEP: char = std::path::MAIN_SEPARATOR;
|
||||||
|
|
||||||
fn create_default_context() -> EngineState {
|
|
||||||
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context())
|
|
||||||
}
|
|
||||||
|
|
||||||
// creates a new engine with the current path into the completions fixtures folder
|
// creates a new engine with the current path into the completions fixtures folder
|
||||||
pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
|
pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
|
||||||
// Target folder inside assets
|
// Target folder inside assets
|
||||||
@@ -36,69 +33,20 @@ pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
|
|||||||
"PWD".to_string(),
|
"PWD".to_string(),
|
||||||
Value::String {
|
Value::String {
|
||||||
val: dir_str.clone(),
|
val: dir_str.clone(),
|
||||||
span: nu_protocol::Span::new(0, dir_str.len()),
|
span: nu_protocol::Span {
|
||||||
|
start: 0,
|
||||||
|
end: dir_str.len(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
stack.add_env_var(
|
stack.add_env_var(
|
||||||
"TEST".to_string(),
|
"TEST".to_string(),
|
||||||
Value::String {
|
Value::String {
|
||||||
val: "NUSHELL".to_string(),
|
val: "NUSHELL".to_string(),
|
||||||
span: nu_protocol::Span::new(0, dir_str.len()),
|
span: nu_protocol::Span {
|
||||||
},
|
start: 0,
|
||||||
);
|
end: dir_str.len(),
|
||||||
#[cfg(windows)]
|
},
|
||||||
stack.add_env_var(
|
|
||||||
"Path".to_string(),
|
|
||||||
Value::String {
|
|
||||||
val: "c:\\some\\path;c:\\some\\other\\path".to_string(),
|
|
||||||
span: nu_protocol::Span::new(0, dir_str.len()),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
stack.add_env_var(
|
|
||||||
"PATH".to_string(),
|
|
||||||
Value::String {
|
|
||||||
val: "/some/path:/some/other/path".to_string(),
|
|
||||||
span: nu_protocol::Span::new(0, dir_str.len()),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Merge environment into the permanent state
|
|
||||||
let merge_result = engine_state.merge_env(&mut stack, &dir);
|
|
||||||
assert!(merge_result.is_ok());
|
|
||||||
|
|
||||||
(dir, dir_str, engine_state, stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
|
|
||||||
// Target folder inside assets
|
|
||||||
let dir = fs::fixtures().join("quoted_completions");
|
|
||||||
let mut dir_str = dir
|
|
||||||
.clone()
|
|
||||||
.into_os_string()
|
|
||||||
.into_string()
|
|
||||||
.unwrap_or_default();
|
|
||||||
dir_str.push(SEP);
|
|
||||||
|
|
||||||
// Create a new engine with default context
|
|
||||||
let mut engine_state = create_default_context();
|
|
||||||
|
|
||||||
// New stack
|
|
||||||
let mut stack = Stack::new();
|
|
||||||
|
|
||||||
// Add pwd as env var
|
|
||||||
stack.add_env_var(
|
|
||||||
"PWD".to_string(),
|
|
||||||
Value::String {
|
|
||||||
val: dir_str.clone(),
|
|
||||||
span: nu_protocol::Span::new(0, dir_str.len()),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
stack.add_env_var(
|
|
||||||
"TEST".to_string(),
|
|
||||||
Value::String {
|
|
||||||
val: "NUSHELL".to_string(),
|
|
||||||
span: nu_protocol::Span::new(0, dir_str.len()),
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -149,9 +97,9 @@ pub fn merge_input(
|
|||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
let block = parse(&mut working_set, None, input, false);
|
let (block, err) = parse(&mut working_set, None, input, false, &[]);
|
||||||
|
|
||||||
assert!(working_set.parse_errors.is_empty());
|
assert!(err.is_none());
|
||||||
|
|
||||||
(block, working_set.render())
|
(block, working_set.render())
|
||||||
};
|
};
|
||||||
@@ -164,7 +112,7 @@ pub fn merge_input(
|
|||||||
&block,
|
&block,
|
||||||
PipelineData::Value(
|
PipelineData::Value(
|
||||||
Value::Nothing {
|
Value::Nothing {
|
||||||
span: Span::unknown(),
|
span: Span { start: 0, end: 0 },
|
||||||
},
|
},
|
||||||
None
|
None
|
||||||
),
|
),
|
||||||
|
@@ -1,15 +0,0 @@
|
|||||||
[package]
|
|
||||||
authors = ["The Nushell Project Developers"]
|
|
||||||
description = "The foundation tools to build Nushell commands."
|
|
||||||
edition = "2021"
|
|
||||||
license = "MIT"
|
|
||||||
name = "nu-cmd-base"
|
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
|
|
||||||
version = "0.82.0"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
nu-engine = { path = "../nu-engine", version = "0.82.0" }
|
|
||||||
nu-path = { path = "../nu-path", version = "0.82.0" }
|
|
||||||
nu-protocol = { version = "0.82.0", path = "../nu-protocol" }
|
|
@@ -1,2 +0,0 @@
|
|||||||
pub mod input_handler;
|
|
||||||
pub mod util;
|
|
@@ -1,57 +0,0 @@
|
|||||||
use nu_protocol::report_error;
|
|
||||||
use nu_protocol::{
|
|
||||||
ast::RangeInclusion,
|
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
|
||||||
Range, ShellError, Span, Value,
|
|
||||||
};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
pub fn get_init_cwd() -> PathBuf {
|
|
||||||
std::env::current_dir().unwrap_or_else(|_| {
|
|
||||||
std::env::var("PWD")
|
|
||||||
.map(Into::into)
|
|
||||||
.unwrap_or_else(|_| nu_path::home_dir().unwrap_or_default())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
|
|
||||||
nu_engine::env::current_dir(engine_state, stack).unwrap_or_else(|e| {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
report_error(&working_set, &e);
|
|
||||||
crate::util::get_init_cwd()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type MakeRangeError = fn(&str, Span) -> ShellError;
|
|
||||||
|
|
||||||
pub fn process_range(range: &Range) -> Result<(isize, isize), MakeRangeError> {
|
|
||||||
let start = match &range.from {
|
|
||||||
Value::Int { val, .. } => isize::try_from(*val).unwrap_or_default(),
|
|
||||||
Value::Nothing { .. } => 0,
|
|
||||||
_ => {
|
|
||||||
return Err(|msg, span| ShellError::TypeMismatch {
|
|
||||||
err_message: msg.to_string(),
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let end = match &range.to {
|
|
||||||
Value::Int { val, .. } => {
|
|
||||||
if matches!(range.inclusion, RangeInclusion::Inclusive) {
|
|
||||||
isize::try_from(*val).unwrap_or(isize::max_value())
|
|
||||||
} else {
|
|
||||||
isize::try_from(*val).unwrap_or(isize::max_value()) - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Nothing { .. } => isize::max_value(),
|
|
||||||
_ => {
|
|
||||||
return Err(|msg, span| ShellError::TypeMismatch {
|
|
||||||
err_message: msg.to_string(),
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((start, end))
|
|
||||||
}
|
|
@@ -1,67 +0,0 @@
|
|||||||
[package]
|
|
||||||
authors = ["The Nushell Project Developers"]
|
|
||||||
description = "Nushell's dataframe commands based on polars."
|
|
||||||
edition = "2021"
|
|
||||||
license = "MIT"
|
|
||||||
name = "nu-cmd-dataframe"
|
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-dataframe"
|
|
||||||
version = "0.82.0"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
bench = false
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
nu-engine = { path = "../nu-engine", version = "0.82.0" }
|
|
||||||
nu-parser = { path = "../nu-parser", version = "0.82.0" }
|
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.82.0" }
|
|
||||||
|
|
||||||
# Potential dependencies for extras
|
|
||||||
chrono = { version = "0.4", features = [
|
|
||||||
"std",
|
|
||||||
"unstable-locales",
|
|
||||||
], default-features = false }
|
|
||||||
fancy-regex = "0.11"
|
|
||||||
indexmap = { version = "1.7", features = ["serde-1"] }
|
|
||||||
num = { version = "0.4", optional = true }
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
|
||||||
sqlparser = { version = "0.33", features = ["serde"], optional = true }
|
|
||||||
|
|
||||||
[dependencies.polars]
|
|
||||||
features = [
|
|
||||||
"arg_where",
|
|
||||||
"checked_arithmetic",
|
|
||||||
"concat_str",
|
|
||||||
"cross_join",
|
|
||||||
"csv",
|
|
||||||
"cum_agg",
|
|
||||||
"default",
|
|
||||||
"dtype-categorical",
|
|
||||||
"dtype-datetime",
|
|
||||||
"dtype-struct",
|
|
||||||
"dynamic_groupby",
|
|
||||||
"ipc",
|
|
||||||
"is_in",
|
|
||||||
"json",
|
|
||||||
"lazy",
|
|
||||||
"object",
|
|
||||||
"parquet",
|
|
||||||
"random",
|
|
||||||
"rolling_window",
|
|
||||||
"rows",
|
|
||||||
"serde",
|
|
||||||
"serde-lazy",
|
|
||||||
"strings",
|
|
||||||
"to_dummies",
|
|
||||||
]
|
|
||||||
optional = true
|
|
||||||
version = "0.30.0"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
dataframe = ["default"]
|
|
||||||
default = ["num", "polars", "sqlparser"]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.82.0" }
|
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.82.0" }
|
|
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2019 - 2023 The Nushell Project Developers
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
@@ -1,97 +0,0 @@
|
|||||||
use std::{fs::File, io::BufWriter, path::PathBuf};
|
|
||||||
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::{
|
|
||||||
ast::Call,
|
|
||||||
engine::{Command, EngineState, Stack},
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
use polars::prelude::{JsonWriter, SerWriter};
|
|
||||||
|
|
||||||
use super::super::values::NuDataFrame;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ToJsonLines;
|
|
||||||
|
|
||||||
impl Command for ToJsonLines {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"dfr to-jsonl"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Saves dataframe to a JSON lines file."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build(self.name())
|
|
||||||
.required("file", SyntaxShape::Filepath, "file path to save dataframe")
|
|
||||||
.input_type(Type::Custom("dataframe".into()))
|
|
||||||
.output_type(Type::Any)
|
|
||||||
.category(Category::Custom("dataframe".into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Saves dataframe to JSON lines file",
|
|
||||||
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr to-jsonl test.jsonl",
|
|
||||||
result: None,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
command(engine_state, stack, call, input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn command(
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let file_name: Spanned<PathBuf> = call.req(engine_state, stack, 0)?;
|
|
||||||
|
|
||||||
let mut df = NuDataFrame::try_from_pipeline(input, call.head)?;
|
|
||||||
|
|
||||||
let file = File::create(&file_name.item).map_err(|e| {
|
|
||||||
ShellError::GenericError(
|
|
||||||
"Error with file name".into(),
|
|
||||||
e.to_string(),
|
|
||||||
Some(file_name.span),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
let buf_writer = BufWriter::new(file);
|
|
||||||
|
|
||||||
JsonWriter::new(buf_writer)
|
|
||||||
.finish(df.as_mut())
|
|
||||||
.map_err(|e| {
|
|
||||||
ShellError::GenericError(
|
|
||||||
"Error saving file".into(),
|
|
||||||
e.to_string(),
|
|
||||||
Some(file_name.span),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let file_value = Value::String {
|
|
||||||
val: format!("saved {:?}", &file_name.item),
|
|
||||||
span: file_name.span,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(PipelineData::Value(
|
|
||||||
Value::List {
|
|
||||||
vals: vec![file_value],
|
|
||||||
span: call.head,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
|
@@ -1,162 +0,0 @@
|
|||||||
use super::super::values::NuExpression;
|
|
||||||
|
|
||||||
use crate::dataframe::values::{Column, NuDataFrame};
|
|
||||||
use chrono::{DateTime, FixedOffset};
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::{
|
|
||||||
ast::Call,
|
|
||||||
engine::{Command, EngineState, Stack},
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
|
||||||
Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ExprDatePart;
|
|
||||||
|
|
||||||
impl Command for ExprDatePart {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"dfr datepart"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Creates an expression for capturing the specified datepart in a column."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build(self.name())
|
|
||||||
.required(
|
|
||||||
"Datepart name",
|
|
||||||
SyntaxShape::String,
|
|
||||||
"Part of the date to capture. Possible values are year, quarter, month, week, weekday, day, hour, minute, second, millisecond, microsecond, nanosecond",
|
|
||||||
)
|
|
||||||
.input_type(Type::Custom("expression".into()))
|
|
||||||
.output_type(Type::Custom("expression".into()))
|
|
||||||
.category(Category::Custom("expression".into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
let dt = DateTime::<FixedOffset>::parse_from_str(
|
|
||||||
"2021-12-30T01:02:03.123456789 +0000",
|
|
||||||
"%Y-%m-%dT%H:%M:%S.%9f %z",
|
|
||||||
)
|
|
||||||
.expect("date calculation should not fail in test");
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Creates an expression to capture the year date part",
|
|
||||||
example: r#"[["2021-12-30T01:02:03.123456789"]] | dfr into-df | dfr as-datetime "%Y-%m-%dT%H:%M:%S.%9f" | dfr with-column [(dfr col datetime | dfr datepart year | dfr as datetime_year )]"#,
|
|
||||||
result: Some(
|
|
||||||
NuDataFrame::try_from_columns(vec![
|
|
||||||
Column::new("datetime".to_string(), vec![Value::test_date(dt)]),
|
|
||||||
Column::new("datetime_year".to_string(), vec![Value::test_int(2021)]),
|
|
||||||
])
|
|
||||||
.expect("simple df for test should not fail")
|
|
||||||
.into_value(Span::test_data()),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Creates an expression to capture multiple date parts",
|
|
||||||
example: r#"[["2021-12-30T01:02:03.123456789"]] | dfr into-df | dfr as-datetime "%Y-%m-%dT%H:%M:%S.%9f" |
|
|
||||||
dfr with-column [ (dfr col datetime | dfr datepart year | dfr as datetime_year ),
|
|
||||||
(dfr col datetime | dfr datepart month | dfr as datetime_month ),
|
|
||||||
(dfr col datetime | dfr datepart day | dfr as datetime_day ),
|
|
||||||
(dfr col datetime | dfr datepart hour | dfr as datetime_hour ),
|
|
||||||
(dfr col datetime | dfr datepart minute | dfr as datetime_minute ),
|
|
||||||
(dfr col datetime | dfr datepart second | dfr as datetime_second ),
|
|
||||||
(dfr col datetime | dfr datepart nanosecond | dfr as datetime_ns ) ]"#,
|
|
||||||
result: Some(
|
|
||||||
NuDataFrame::try_from_columns(vec![
|
|
||||||
Column::new("datetime".to_string(), vec![Value::test_date(dt)]),
|
|
||||||
Column::new("datetime_year".to_string(), vec![Value::test_int(2021)]),
|
|
||||||
Column::new("datetime_month".to_string(), vec![Value::test_int(12)]),
|
|
||||||
Column::new("datetime_day".to_string(), vec![Value::test_int(30)]),
|
|
||||||
Column::new("datetime_hour".to_string(), vec![Value::test_int(1)]),
|
|
||||||
Column::new("datetime_minute".to_string(), vec![Value::test_int(2)]),
|
|
||||||
Column::new("datetime_second".to_string(), vec![Value::test_int(3)]),
|
|
||||||
Column::new("datetime_ns".to_string(), vec![Value::test_int(123456789)]),
|
|
||||||
])
|
|
||||||
.expect("simple df for test should not fail")
|
|
||||||
.into_value(Span::test_data()),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec![
|
|
||||||
"year",
|
|
||||||
"month",
|
|
||||||
"week",
|
|
||||||
"weekday",
|
|
||||||
"quarter",
|
|
||||||
"day",
|
|
||||||
"hour",
|
|
||||||
"minute",
|
|
||||||
"second",
|
|
||||||
"millisecond",
|
|
||||||
"microsecond",
|
|
||||||
"nanosecond",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let part: Spanned<String> = call.req(engine_state, stack, 0)?;
|
|
||||||
|
|
||||||
let expr = NuExpression::try_from_pipeline(input, call.head)?;
|
|
||||||
let expr_dt = expr.into_polars().dt();
|
|
||||||
let expr = match part.item.as_str() {
|
|
||||||
"year" => expr_dt.year(),
|
|
||||||
"quarter" => expr_dt.quarter(),
|
|
||||||
"month" => expr_dt.month(),
|
|
||||||
"week" => expr_dt.week(),
|
|
||||||
"day" => expr_dt.day(),
|
|
||||||
"hour" => expr_dt.hour(),
|
|
||||||
"minute" => expr_dt.minute(),
|
|
||||||
"second" => expr_dt.second(),
|
|
||||||
"millisecond" => expr_dt.millisecond(),
|
|
||||||
"microsecond" => expr_dt.microsecond(),
|
|
||||||
"nanosecond" => expr_dt.nanosecond(),
|
|
||||||
_ => {
|
|
||||||
return Err(ShellError::UnsupportedInput(
|
|
||||||
format!("{} is not a valid datepart, expected one of year, month, day, hour, minute, second, millisecond, microsecond, nanosecond", part.item),
|
|
||||||
"value originates from here".to_string(),
|
|
||||||
call.head,
|
|
||||||
part.span,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}.into();
|
|
||||||
|
|
||||||
Ok(PipelineData::Value(
|
|
||||||
NuExpression::into_value(expr, call.head),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::super::super::test_dataframe::test_dataframe;
|
|
||||||
use super::*;
|
|
||||||
use crate::dataframe::eager::WithColumn;
|
|
||||||
use crate::dataframe::expressions::ExprAlias;
|
|
||||||
use crate::dataframe::expressions::ExprAsNu;
|
|
||||||
use crate::dataframe::expressions::ExprCol;
|
|
||||||
use crate::dataframe::series::AsDateTime;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
test_dataframe(vec![
|
|
||||||
Box::new(ExprDatePart {}),
|
|
||||||
Box::new(ExprCol {}),
|
|
||||||
Box::new(ExprAsNu {}),
|
|
||||||
Box::new(AsDateTime {}),
|
|
||||||
Box::new(WithColumn {}),
|
|
||||||
Box::new(ExprAlias {}),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,137 +0,0 @@
|
|||||||
use crate::dataframe::values::{Column, NuDataFrame, NuExpression};
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::{
|
|
||||||
ast::Call,
|
|
||||||
engine::{Command, EngineState, Stack},
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct LazyFillNA;
|
|
||||||
|
|
||||||
impl Command for LazyFillNA {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"dfr fill-nan"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Replaces NaN values with the given expression."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build(self.name())
|
|
||||||
.required(
|
|
||||||
"fill",
|
|
||||||
SyntaxShape::Any,
|
|
||||||
"Expression to use to fill the NAN values",
|
|
||||||
)
|
|
||||||
.input_type(Type::Custom("dataframe".into()))
|
|
||||||
.output_type(Type::Custom("dataframe".into()))
|
|
||||||
.category(Category::Custom("lazyframe".into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Fills the NaN values with 0",
|
|
||||||
example: "[1 2 NaN 3 NaN] | dfr into-df | dfr fill-nan 0",
|
|
||||||
result: Some(
|
|
||||||
NuDataFrame::try_from_columns(vec![Column::new(
|
|
||||||
"0".to_string(),
|
|
||||||
vec![
|
|
||||||
Value::test_int(1),
|
|
||||||
Value::test_int(2),
|
|
||||||
Value::test_int(0),
|
|
||||||
Value::test_int(3),
|
|
||||||
Value::test_int(0),
|
|
||||||
],
|
|
||||||
)])
|
|
||||||
.expect("Df for test should not fail")
|
|
||||||
.into_value(Span::test_data()),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Fills the NaN values of a whole dataframe",
|
|
||||||
example: "[[a b]; [0.2 1] [0.1 NaN]] | dfr into-df | dfr fill-nan 0",
|
|
||||||
result: Some(
|
|
||||||
NuDataFrame::try_from_columns(vec![
|
|
||||||
Column::new(
|
|
||||||
"a".to_string(),
|
|
||||||
vec![Value::test_float(0.2), Value::test_float(0.1)],
|
|
||||||
),
|
|
||||||
Column::new(
|
|
||||||
"b".to_string(),
|
|
||||||
vec![Value::test_int(1), Value::test_int(0)],
|
|
||||||
),
|
|
||||||
])
|
|
||||||
.expect("Df for test should not fail")
|
|
||||||
.into_value(Span::test_data()),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let fill: Value = call.req(engine_state, stack, 0)?;
|
|
||||||
let value = input.into_value(call.head);
|
|
||||||
|
|
||||||
if NuExpression::can_downcast(&value) {
|
|
||||||
let expr = NuExpression::try_from_value(value)?;
|
|
||||||
let fill = NuExpression::try_from_value(fill)?.into_polars();
|
|
||||||
let expr: NuExpression = expr.into_polars().fill_nan(fill).into();
|
|
||||||
|
|
||||||
Ok(PipelineData::Value(
|
|
||||||
NuExpression::into_value(expr, call.head),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
let val_span = value.span()?;
|
|
||||||
let frame = NuDataFrame::try_from_value(value)?;
|
|
||||||
let columns = frame.columns(val_span)?;
|
|
||||||
let dataframe = columns
|
|
||||||
.into_iter()
|
|
||||||
.map(|column| {
|
|
||||||
let column_name = column.name().to_string();
|
|
||||||
let values = column
|
|
||||||
.into_iter()
|
|
||||||
.map(|value| match value {
|
|
||||||
Value::Float { val, .. } => {
|
|
||||||
if val.is_nan() {
|
|
||||||
fill.clone()
|
|
||||||
} else {
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::List { vals, span } => {
|
|
||||||
NuDataFrame::fill_list_nan(vals, span, fill.clone())
|
|
||||||
}
|
|
||||||
_ => value,
|
|
||||||
})
|
|
||||||
.collect::<Vec<Value>>();
|
|
||||||
Column::new(column_name, values)
|
|
||||||
})
|
|
||||||
.collect::<Vec<Column>>();
|
|
||||||
Ok(PipelineData::Value(
|
|
||||||
NuDataFrame::try_from_columns(dataframe)?.into_value(call.head),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::super::super::test_dataframe::test_dataframe;
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
test_dataframe(vec![Box::new(LazyFillNA {})])
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,34 +0,0 @@
|
|||||||
mod eager;
|
|
||||||
mod expressions;
|
|
||||||
mod lazy;
|
|
||||||
mod series;
|
|
||||||
mod utils;
|
|
||||||
mod values;
|
|
||||||
|
|
||||||
pub use eager::add_eager_decls;
|
|
||||||
pub use expressions::add_expressions;
|
|
||||||
pub use lazy::add_lazy_decls;
|
|
||||||
pub use series::add_series_decls;
|
|
||||||
|
|
||||||
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
|
||||||
|
|
||||||
pub fn add_dataframe_context(mut engine_state: EngineState) -> EngineState {
|
|
||||||
let delta = {
|
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
|
||||||
add_series_decls(&mut working_set);
|
|
||||||
add_eager_decls(&mut working_set);
|
|
||||||
add_expressions(&mut working_set);
|
|
||||||
add_lazy_decls(&mut working_set);
|
|
||||||
|
|
||||||
working_set.render()
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(err) = engine_state.merge_delta(delta) {
|
|
||||||
eprintln!("Error creating dataframe command context: {err:?}");
|
|
||||||
}
|
|
||||||
|
|
||||||
engine_state
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_dataframe;
|
|
@@ -1,2 +0,0 @@
|
|||||||
pub mod dataframe;
|
|
||||||
pub use dataframe::*;
|
|
@@ -1,31 +0,0 @@
|
|||||||
[package]
|
|
||||||
authors = ["The Nushell Project Developers"]
|
|
||||||
description = "Nushell's extra commands that are not part of the 1.0 api standard."
|
|
||||||
edition = "2021"
|
|
||||||
license = "MIT"
|
|
||||||
name = "nu-cmd-extra"
|
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
|
|
||||||
version = "0.82.0"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
bench = false
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
nu-engine = { path = "../nu-engine", version = "0.82.0" }
|
|
||||||
nu-parser = { path = "../nu-parser", version = "0.82.0" }
|
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.82.0" }
|
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.82.0" }
|
|
||||||
nu-utils = { path = "../nu-utils", version = "0.82.0" }
|
|
||||||
|
|
||||||
# Potential dependencies for extras
|
|
||||||
num-traits = "0.2"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
extra = ["default"]
|
|
||||||
default = []
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.82.0" }
|
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.82.0" }
|
|
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2019 - 2023 The Nushell Project Developers
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
@@ -1,74 +0,0 @@
|
|||||||
#[cfg(test)]
|
|
||||||
use nu_protocol::engine::Command;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub fn test_examples(cmd: impl Command + 'static) {
|
|
||||||
test_examples::test_examples(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_examples {
|
|
||||||
|
|
||||||
use nu_cmd_lang::example_support::{
|
|
||||||
check_all_signature_input_output_types_entries_have_examples,
|
|
||||||
check_example_evaluates_to_expected_output,
|
|
||||||
check_example_input_and_output_types_match_command_signature,
|
|
||||||
};
|
|
||||||
|
|
||||||
use nu_protocol::{
|
|
||||||
engine::{Command, EngineState, StateWorkingSet},
|
|
||||||
Type,
|
|
||||||
};
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
pub fn test_examples(cmd: impl Command + 'static) {
|
|
||||||
let examples = cmd.examples();
|
|
||||||
let signature = cmd.signature();
|
|
||||||
let mut engine_state = make_engine_state(cmd.clone_box());
|
|
||||||
|
|
||||||
let cwd = std::env::current_dir().expect("Could not get current working directory.");
|
|
||||||
|
|
||||||
let mut witnessed_type_transformations = HashSet::<(Type, Type)>::new();
|
|
||||||
|
|
||||||
for example in examples {
|
|
||||||
if example.result.is_none() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
witnessed_type_transformations.extend(
|
|
||||||
check_example_input_and_output_types_match_command_signature(
|
|
||||||
&example,
|
|
||||||
&cwd,
|
|
||||||
&mut make_engine_state(cmd.clone_box()),
|
|
||||||
&signature.input_output_types,
|
|
||||||
signature.operates_on_cell_paths(),
|
|
||||||
signature.vectorizes_over_list,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
check_example_evaluates_to_expected_output(&example, cwd.as_path(), &mut engine_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
check_all_signature_input_output_types_entries_have_examples(
|
|
||||||
signature,
|
|
||||||
witnessed_type_transformations,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_engine_state(cmd: Box<dyn Command>) -> Box<EngineState> {
|
|
||||||
let mut engine_state = Box::new(EngineState::new());
|
|
||||||
|
|
||||||
let delta = {
|
|
||||||
// Base functions that are needed for testing
|
|
||||||
// Try to keep this working set small to keep tests running as fast as possible
|
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
|
||||||
|
|
||||||
// Adding the command that is being tested to the working set
|
|
||||||
working_set.add_decl(cmd);
|
|
||||||
working_set.render()
|
|
||||||
};
|
|
||||||
|
|
||||||
engine_state
|
|
||||||
.merge_delta(delta)
|
|
||||||
.expect("Error merging delta");
|
|
||||||
engine_state
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,210 +0,0 @@
|
|||||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
|
||||||
use nu_cmd_base::util;
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::{
|
|
||||||
ast::{Call, CellPath},
|
|
||||||
engine::{Command, EngineState, Stack},
|
|
||||||
Category, Example, PipelineData, Range, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct BytesAt;
|
|
||||||
|
|
||||||
struct Arguments {
|
|
||||||
indexes: Subbytes,
|
|
||||||
cell_paths: Option<Vec<CellPath>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CmdArgument for Arguments {
|
|
||||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
|
||||||
self.cell_paths.take()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<(isize, isize)> for Subbytes {
|
|
||||||
fn from(input: (isize, isize)) -> Self {
|
|
||||||
Self(input.0, input.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
struct Subbytes(isize, isize);
|
|
||||||
|
|
||||||
impl Command for BytesAt {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"bytes at"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("bytes at")
|
|
||||||
.input_output_types(vec![(Type::Binary, Type::Binary)])
|
|
||||||
.vectorizes_over_list(true)
|
|
||||||
.required("range", SyntaxShape::Range, "the range to get bytes")
|
|
||||||
.rest(
|
|
||||||
"rest",
|
|
||||||
SyntaxShape::CellPath,
|
|
||||||
"for a data structure input, get bytes from data at the given cell paths",
|
|
||||||
)
|
|
||||||
.category(Category::Bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Get bytes defined by a range"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["slice"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let range: Range = call.req(engine_state, stack, 0)?;
|
|
||||||
let indexes = match util::process_range(&range) {
|
|
||||||
Ok(idxs) => idxs.into(),
|
|
||||||
Err(processing_error) => {
|
|
||||||
return Err(processing_error("could not perform subbytes", call.head));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
|
||||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
|
||||||
let args = Arguments {
|
|
||||||
indexes,
|
|
||||||
cell_paths,
|
|
||||||
};
|
|
||||||
|
|
||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Get a subbytes `0x[10 01]` from the bytes `0x[33 44 55 10 01 13]`",
|
|
||||||
example: " 0x[33 44 55 10 01 13] | bytes at 3..<4",
|
|
||||||
result: Some(Value::Binary {
|
|
||||||
val: vec![0x10],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Get a subbytes `0x[10 01 13]` from the bytes `0x[33 44 55 10 01 13]`",
|
|
||||||
example: " 0x[33 44 55 10 01 13] | bytes at 3..6",
|
|
||||||
result: Some(Value::Binary {
|
|
||||||
val: vec![0x10, 0x01, 0x13],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Get the remaining characters from a starting index",
|
|
||||||
example: " 0x[33 44 55 10 01 13] | bytes at 3..",
|
|
||||||
result: Some(Value::Binary {
|
|
||||||
val: vec![0x10, 0x01, 0x13],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Get the characters from the beginning until ending index",
|
|
||||||
example: " 0x[33 44 55 10 01 13] | bytes at ..<4",
|
|
||||||
result: Some(Value::Binary {
|
|
||||||
val: vec![0x33, 0x44, 0x55, 0x10],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description:
|
|
||||||
"Or the characters from the beginning until ending index inside a table",
|
|
||||||
example: r#" [[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes at 1.. ColB ColC"#,
|
|
||||||
result: Some(Value::List {
|
|
||||||
vals: vec![Value::Record {
|
|
||||||
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
|
||||||
vals: vec![
|
|
||||||
Value::Binary {
|
|
||||||
val: vec![0x11, 0x12, 0x13],
|
|
||||||
span: Span::test_data(),
|
|
||||||
},
|
|
||||||
Value::Binary {
|
|
||||||
val: vec![0x15, 0x16],
|
|
||||||
span: Span::test_data(),
|
|
||||||
},
|
|
||||||
Value::Binary {
|
|
||||||
val: vec![0x18, 0x19],
|
|
||||||
span: Span::test_data(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
|
||||||
let range = &args.indexes;
|
|
||||||
match input {
|
|
||||||
Value::Binary { val, .. } => {
|
|
||||||
use std::cmp::{self, Ordering};
|
|
||||||
let len = val.len() as isize;
|
|
||||||
|
|
||||||
let start = if range.0 < 0 { range.0 + len } else { range.0 };
|
|
||||||
|
|
||||||
let end = if range.1 < 0 {
|
|
||||||
cmp::max(range.1 + len, 0)
|
|
||||||
} else {
|
|
||||||
range.1
|
|
||||||
};
|
|
||||||
|
|
||||||
if start < len && end >= 0 {
|
|
||||||
match start.cmp(&end) {
|
|
||||||
Ordering::Equal => Value::Binary {
|
|
||||||
val: vec![],
|
|
||||||
span: head,
|
|
||||||
},
|
|
||||||
Ordering::Greater => Value::Error {
|
|
||||||
error: Box::new(ShellError::TypeMismatch {
|
|
||||||
err_message: "End must be greater than or equal to Start".to_string(),
|
|
||||||
span: head,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Ordering::Less => Value::Binary {
|
|
||||||
val: {
|
|
||||||
if end == isize::max_value() {
|
|
||||||
val.iter().skip(start as usize).copied().collect()
|
|
||||||
} else {
|
|
||||||
val.iter()
|
|
||||||
.skip(start as usize)
|
|
||||||
.take((end - start) as usize)
|
|
||||||
.copied()
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
span: head,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Value::Binary {
|
|
||||||
val: vec![],
|
|
||||||
span: head,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::Error { .. } => input.clone(),
|
|
||||||
|
|
||||||
other => Value::Error {
|
|
||||||
error: Box::new(ShellError::UnsupportedInput(
|
|
||||||
"Only binary values are supported".into(),
|
|
||||||
format!("input type: {:?}", other.get_type()),
|
|
||||||
head,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,172 +0,0 @@
|
|||||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::ast::CellPath;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::Category;
|
|
||||||
use nu_protocol::IntoPipelineData;
|
|
||||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
|
||||||
|
|
||||||
struct Arguments {
|
|
||||||
pattern: Vec<u8>,
|
|
||||||
cell_paths: Option<Vec<CellPath>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CmdArgument for Arguments {
|
|
||||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
|
||||||
self.cell_paths.take()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
|
|
||||||
pub struct BytesStartsWith;
|
|
||||||
|
|
||||||
impl Command for BytesStartsWith {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"bytes starts-with"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("bytes starts-with")
|
|
||||||
.input_output_types(vec![(Type::Binary, Type::Bool)])
|
|
||||||
.required("pattern", SyntaxShape::Binary, "the pattern to match")
|
|
||||||
.rest(
|
|
||||||
"rest",
|
|
||||||
SyntaxShape::CellPath,
|
|
||||||
"for a data structure input, check if bytes at the given cell paths start with the pattern",
|
|
||||||
)
|
|
||||||
.category(Category::Bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Check if bytes starts with a pattern."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["pattern", "match", "find", "search"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
|
||||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
|
||||||
let arg = Arguments {
|
|
||||||
pattern,
|
|
||||||
cell_paths,
|
|
||||||
};
|
|
||||||
|
|
||||||
match input {
|
|
||||||
PipelineData::ExternalStream {
|
|
||||||
stdout: Some(stream),
|
|
||||||
span,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let mut i = 0;
|
|
||||||
|
|
||||||
for item in stream {
|
|
||||||
let byte_slice = match &item {
|
|
||||||
// String and binary data are valid byte patterns
|
|
||||||
Ok(Value::String { val, .. }) => val.as_bytes(),
|
|
||||||
Ok(Value::Binary { val, .. }) => val,
|
|
||||||
// If any Error value is output, echo it back
|
|
||||||
Ok(v @ Value::Error { .. }) => return Ok(v.clone().into_pipeline_data()),
|
|
||||||
// Unsupported data
|
|
||||||
Ok(other) => {
|
|
||||||
return Ok(Value::Error {
|
|
||||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
|
||||||
exp_input_type: "string and binary".into(),
|
|
||||||
wrong_type: other.get_type().to_string(),
|
|
||||||
dst_span: span,
|
|
||||||
src_span: other.expect_span(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
.into_pipeline_data());
|
|
||||||
}
|
|
||||||
Err(err) => return Err(err.to_owned()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let max = byte_slice.len().min(arg.pattern.len() - i);
|
|
||||||
|
|
||||||
if byte_slice[..max] == arg.pattern[i..i + max] {
|
|
||||||
i += max;
|
|
||||||
|
|
||||||
if i >= arg.pattern.len() {
|
|
||||||
return Ok(Value::boolean(true, span).into_pipeline_data());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Ok(Value::boolean(false, span).into_pipeline_data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We reached the end of the stream and never returned,
|
|
||||||
// the pattern wasn't exhausted so it probably doesn't match
|
|
||||||
Ok(Value::boolean(false, span).into_pipeline_data())
|
|
||||||
}
|
|
||||||
_ => operate(
|
|
||||||
starts_with,
|
|
||||||
arg,
|
|
||||||
input,
|
|
||||||
call.head,
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Checks if binary starts with `0x[1F FF AA]`",
|
|
||||||
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F FF AA]",
|
|
||||||
result: Some(Value::test_bool(true)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Checks if binary starts with `0x[1F]`",
|
|
||||||
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F]",
|
|
||||||
result: Some(Value::test_bool(true)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Checks if binary starts with `0x[1F]`",
|
|
||||||
example: "0x[1F FF AA AA] | bytes starts-with 0x[11]",
|
|
||||||
result: Some(Value::test_bool(false)),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn starts_with(val: &Value, args: &Arguments, span: Span) -> Value {
|
|
||||||
match val {
|
|
||||||
Value::Binary {
|
|
||||||
val,
|
|
||||||
span: val_span,
|
|
||||||
} => Value::boolean(val.starts_with(&args.pattern), *val_span),
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => val.clone(),
|
|
||||||
other => Value::Error {
|
|
||||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
|
||||||
exp_input_type: "binary".into(),
|
|
||||||
wrong_type: other.get_type().to_string(),
|
|
||||||
dst_span: span,
|
|
||||||
src_span: other.expect_span(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(BytesStartsWith {})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,78 +0,0 @@
|
|||||||
mod bits;
|
|
||||||
mod bytes;
|
|
||||||
|
|
||||||
pub use bits::Bits;
|
|
||||||
pub use bits::BitsAnd;
|
|
||||||
pub use bits::BitsNot;
|
|
||||||
pub use bits::BitsOr;
|
|
||||||
pub use bits::BitsRol;
|
|
||||||
pub use bits::BitsRor;
|
|
||||||
pub use bits::BitsShl;
|
|
||||||
pub use bits::BitsShr;
|
|
||||||
pub use bits::BitsXor;
|
|
||||||
|
|
||||||
pub use bytes::Bytes;
|
|
||||||
pub use bytes::BytesAdd;
|
|
||||||
pub use bytes::BytesAt;
|
|
||||||
pub use bytes::BytesBuild;
|
|
||||||
pub use bytes::BytesCollect;
|
|
||||||
pub use bytes::BytesEndsWith;
|
|
||||||
pub use bytes::BytesIndexOf;
|
|
||||||
pub use bytes::BytesLen;
|
|
||||||
pub use bytes::BytesRemove;
|
|
||||||
pub use bytes::BytesReplace;
|
|
||||||
pub use bytes::BytesReverse;
|
|
||||||
pub use bytes::BytesStartsWith;
|
|
||||||
|
|
||||||
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
|
||||||
|
|
||||||
pub fn add_extra_command_context(mut engine_state: EngineState) -> EngineState {
|
|
||||||
let delta = {
|
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
|
||||||
|
|
||||||
macro_rules! bind_command {
|
|
||||||
( $command:expr ) => {
|
|
||||||
working_set.add_decl(Box::new($command));
|
|
||||||
};
|
|
||||||
( $( $command:expr ),* ) => {
|
|
||||||
$( working_set.add_decl(Box::new($command)); )*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
bind_command! {
|
|
||||||
Bits,
|
|
||||||
BitsAnd,
|
|
||||||
BitsNot,
|
|
||||||
BitsOr,
|
|
||||||
BitsXor,
|
|
||||||
BitsRol,
|
|
||||||
BitsRor,
|
|
||||||
BitsShl,
|
|
||||||
BitsShr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes
|
|
||||||
bind_command! {
|
|
||||||
Bytes,
|
|
||||||
BytesLen,
|
|
||||||
BytesStartsWith,
|
|
||||||
BytesEndsWith,
|
|
||||||
BytesReverse,
|
|
||||||
BytesReplace,
|
|
||||||
BytesAdd,
|
|
||||||
BytesAt,
|
|
||||||
BytesIndexOf,
|
|
||||||
BytesCollect,
|
|
||||||
BytesRemove,
|
|
||||||
BytesBuild
|
|
||||||
}
|
|
||||||
|
|
||||||
working_set.render()
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(err) = engine_state.merge_delta(delta) {
|
|
||||||
eprintln!("Error creating extra command context: {err:?}");
|
|
||||||
}
|
|
||||||
|
|
||||||
engine_state
|
|
||||||
}
|
|
@@ -1,6 +0,0 @@
|
|||||||
mod example_test;
|
|
||||||
pub mod extra;
|
|
||||||
pub use extra::*;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub use example_test::test_examples;
|
|
@@ -1 +0,0 @@
|
|||||||
mod starts_with;
|
|
@@ -1,153 +0,0 @@
|
|||||||
use nu_test_support::nu;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn basic_binary_starts_with() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
"hello world" | into binary | bytes starts-with 0x[68 65 6c 6c 6f]
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn basic_string_fails() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
"hello world" | bytes starts-with 0x[68 65 6c 6c 6f]
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(actual.err.contains("Input type not supported"));
|
|
||||||
assert_eq!(actual.out, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn short_stream_binary() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
nu --testbin repeater (0x[01]) 5 | bytes starts-with 0x[010101]
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn short_stream_mismatch() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
nu --testbin repeater (0x[010203]) 5 | bytes starts-with 0x[010204]
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "false");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn short_stream_binary_overflow() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
nu --testbin repeater (0x[01]) 5 | bytes starts-with 0x[010101010101]
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "false");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn long_stream_binary() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
nu --testbin repeater (0x[01]) 32768 | bytes starts-with 0x[010101]
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn long_stream_binary_overflow() {
|
|
||||||
// .. ranges are inclusive..inclusive, so we don't need to +1 to check for an overflow
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
nu --testbin repeater (0x[01]) 32768 | bytes starts-with (0..32768 | each {|| 0x[01] } | bytes collect)
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "false");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn long_stream_binary_exact() {
|
|
||||||
// ranges are inclusive..inclusive, so we don't need to +1 to check for an overflow
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
nu --testbin repeater (0x[01020304]) 8192 | bytes starts-with (0..<8192 | each {|| 0x[01020304] } | bytes collect)
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn long_stream_string_exact() {
|
|
||||||
// ranges are inclusive..inclusive, so we don't need to +1 to check for an overflow
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
nu --testbin repeater hell 8192 | bytes starts-with (0..<8192 | each {|| "hell" | into binary } | bytes collect)
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn long_stream_mixed_exact() {
|
|
||||||
// ranges are inclusive..inclusive, so we don't need to +1 to check for an overflow
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
let binseg = (0..<2048 | each {|| 0x[003d9fbf] } | bytes collect)
|
|
||||||
let strseg = (0..<2048 | each {|| "hell" | into binary } | bytes collect)
|
|
||||||
|
|
||||||
nu --testbin repeat_bytes 003d9fbf 2048 68656c6c 2048 | bytes starts-with (bytes build $binseg $strseg)
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
actual.err, "",
|
|
||||||
"invocation failed. command line limit likely reached"
|
|
||||||
);
|
|
||||||
assert_eq!(actual.out, "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn long_stream_mixed_overflow() {
|
|
||||||
// ranges are inclusive..inclusive, so we don't need to +1 to check for an overflow
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
let binseg = (0..<2048 | each {|| 0x[003d9fbf] } | bytes collect)
|
|
||||||
let strseg = (0..<2048 | each {|| "hell" | into binary } | bytes collect)
|
|
||||||
|
|
||||||
nu --testbin repeat_bytes 003d9fbf 2048 68656c6c 2048 | bytes starts-with (bytes build $binseg $strseg 0x[01])
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
actual.err, "",
|
|
||||||
"invocation failed. command line limit likely reached"
|
|
||||||
);
|
|
||||||
assert_eq!(actual.out, "false");
|
|
||||||
}
|
|
@@ -1,2 +0,0 @@
|
|||||||
#[cfg(feature = "extra")]
|
|
||||||
mod bytes;
|
|
@@ -1 +0,0 @@
|
|||||||
mod commands;
|
|
@@ -1,36 +0,0 @@
|
|||||||
[package]
|
|
||||||
authors = ["The Nushell Project Developers"]
|
|
||||||
build = "build.rs"
|
|
||||||
description = "Nushell's core language commands"
|
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
|
|
||||||
edition = "2021"
|
|
||||||
license = "MIT"
|
|
||||||
name = "nu-cmd-lang"
|
|
||||||
version = "0.82.0"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
bench = false
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
nu-engine = { path = "../nu-engine", version = "0.82.0" }
|
|
||||||
nu-parser = { path = "../nu-parser", version = "0.82.0" }
|
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.82.0" }
|
|
||||||
nu-utils = { path = "../nu-utils", version = "0.82.0" }
|
|
||||||
nu-ansi-term = "0.47.0"
|
|
||||||
|
|
||||||
fancy-regex = "0.11"
|
|
||||||
itertools = "0.10"
|
|
||||||
shadow-rs = { version = "0.23", default-features = false }
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
shadow-rs = { version = "0.23", default-features = false }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
mimalloc = []
|
|
||||||
which-support = []
|
|
||||||
trash-support = []
|
|
||||||
sqlite = []
|
|
||||||
dataframe = []
|
|
||||||
static-link-openssl = []
|
|
||||||
wasi = []
|
|
||||||
extra = []
|
|
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2019 - 2023 The Nushell Project Developers
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
@@ -1,22 +0,0 @@
|
|||||||
# nu-cmd-lang
|
|
||||||
|
|
||||||
## the base language and command crate of nu
|
|
||||||
|
|
||||||
The commands in this crate are the *core commands* of the nu language.
|
|
||||||
It is also the base crate upon which all other command crates sit on
|
|
||||||
top of including:
|
|
||||||
|
|
||||||
* nu-command
|
|
||||||
* nu-cli
|
|
||||||
* nu-cmd-dataframe
|
|
||||||
* nu-cmd-extra
|
|
||||||
|
|
||||||
As time goes on and the nu language develops further in parallel with nushell we will be adding other command crates to the system.
|
|
||||||
|
|
||||||
### What does it mean to be a base crate ?
|
|
||||||
|
|
||||||
A base crate is one with minimal dependencies in our system so that other developers can come along and use this crate without having a lot of baggage in terms of other crates which will bloat their underlying application.
|
|
||||||
|
|
||||||
### Background on nu-cmd-lang
|
|
||||||
|
|
||||||
This crate was designed to be a small, concise set of tools or commands that serve as the *foundation layer* of both nu and nushell. These are the core commands needed to have a nice working version of the *nu language* without all of the support that the other commands provide inside nushell. Prior to the launch of this crate all of our commands were housed in the crate *nu-command*. Moving forward we would like to *slowly* break out the commands in nu-command into different crates; the naming and how this will work and where all the commands will be located is a "work in progress" especially now that the *standard library* is starting to become more popular as a location for commands. As time goes on some of our commands written in rust will be migrated to nu and when this happens they will be moved into the *standard library*.
|
|
@@ -1,104 +0,0 @@
|
|||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Const;
|
|
||||||
|
|
||||||
impl Command for Const {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"const"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Create a parse-time constant."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
|
||||||
Signature::build("const")
|
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
|
||||||
.allow_variants_without_examples(true)
|
|
||||||
.required("const_name", SyntaxShape::VarWithOptType, "constant name")
|
|
||||||
.required(
|
|
||||||
"initial_value",
|
|
||||||
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::MathExpression)),
|
|
||||||
"equals sign followed by constant value",
|
|
||||||
)
|
|
||||||
.category(Category::Core)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
|
||||||
r#"This command is a parser keyword. For details, check:
|
|
||||||
https://www.nushell.sh/book/thinking_in_nu.html"#
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_parser_keyword(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["set", "let"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let var_id = call
|
|
||||||
.positional_nth(0)
|
|
||||||
.expect("checked through parser")
|
|
||||||
.as_var()
|
|
||||||
.expect("internal error: missing variable");
|
|
||||||
|
|
||||||
if let Some(constval) = engine_state.find_constant(var_id, &[]) {
|
|
||||||
// Instead of creating a second copy of the value in the stack, we could change
|
|
||||||
// stack.get_var() to check engine_state.find_constant().
|
|
||||||
stack.add_var(var_id, constval.clone());
|
|
||||||
|
|
||||||
Ok(PipelineData::empty())
|
|
||||||
} else {
|
|
||||||
Err(ShellError::NushellFailedSpanned {
|
|
||||||
msg: "Missing Constant".to_string(),
|
|
||||||
label: "constant not added by the parser".to_string(),
|
|
||||||
span: call.head,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Create a new parse-time constant.",
|
|
||||||
example: "const x = 10",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Create a composite constant value",
|
|
||||||
example: "const x = { a: 10, b: 20 }",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use nu_protocol::engine::CommandType;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(Const {})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_command_type() {
|
|
||||||
assert!(matches!(Const.command_type(), CommandType::Keyword));
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,107 +0,0 @@
|
|||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Describe;
|
|
||||||
|
|
||||||
impl Command for Describe {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"describe"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Describe the type and structure of the value(s) piped in."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("describe")
|
|
||||||
.input_output_types(vec![(Type::Any, Type::String)])
|
|
||||||
.switch(
|
|
||||||
"no-collect",
|
|
||||||
"do not collect streams of structured data",
|
|
||||||
Some('n'),
|
|
||||||
)
|
|
||||||
.category(Category::Core)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
_engine_state: &EngineState,
|
|
||||||
_stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
|
|
||||||
let no_collect: bool = call.has_flag("no-collect");
|
|
||||||
|
|
||||||
let description = match input {
|
|
||||||
PipelineData::ExternalStream { .. } => "raw input".into(),
|
|
||||||
PipelineData::ListStream(_, _) => {
|
|
||||||
if no_collect {
|
|
||||||
"stream".into()
|
|
||||||
} else {
|
|
||||||
let value = input.into_value(head);
|
|
||||||
let base_description = match value {
|
|
||||||
Value::CustomValue { val, .. } => val.value_string(),
|
|
||||||
_ => value.get_type().to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
format!("{base_description} (stream)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let value = input.into_value(head);
|
|
||||||
match value {
|
|
||||||
Value::CustomValue { val, .. } => val.value_string(),
|
|
||||||
_ => value.get_type().to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Value::String {
|
|
||||||
val: description,
|
|
||||||
span: head,
|
|
||||||
}
|
|
||||||
.into_pipeline_data())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Describe the type of a string",
|
|
||||||
example: "'hello' | describe",
|
|
||||||
result: Some(Value::test_string("string")),
|
|
||||||
},
|
|
||||||
/*
|
|
||||||
Example {
|
|
||||||
description: "Describe a stream of data, collecting it first",
|
|
||||||
example: "[1 2 3] | each {|i| $i} | describe",
|
|
||||||
result: Some(Value::test_string("list<int> (stream)")),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Describe the input but do not collect streams",
|
|
||||||
example: "[1 2 3] | each {|i| $i} | describe --no-collect",
|
|
||||||
result: Some(Value::test_string("stream")),
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["type", "typeof", "info", "structure"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use super::Describe;
|
|
||||||
use crate::test_examples;
|
|
||||||
test_examples(Describe {})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,216 +0,0 @@
|
|||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ErrorMake;
|
|
||||||
|
|
||||||
impl Command for ErrorMake {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"error make"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("error make")
|
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Error)])
|
|
||||||
.required(
|
|
||||||
"error_struct",
|
|
||||||
SyntaxShape::Record(vec![]),
|
|
||||||
"the error to create",
|
|
||||||
)
|
|
||||||
.switch(
|
|
||||||
"unspanned",
|
|
||||||
"remove the origin label from the error",
|
|
||||||
Some('u'),
|
|
||||||
)
|
|
||||||
.category(Category::Core)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Create an error."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["panic", "crash", "throw"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let span = call.head;
|
|
||||||
let arg: Value = call.req(engine_state, stack, 0)?;
|
|
||||||
let unspanned = call.has_flag("unspanned");
|
|
||||||
|
|
||||||
let throw_error = if unspanned { None } else { Some(span) };
|
|
||||||
Err(make_error(&arg, throw_error).unwrap_or_else(|| {
|
|
||||||
ShellError::GenericError(
|
|
||||||
"Creating error value not supported.".into(),
|
|
||||||
"unsupported error format".into(),
|
|
||||||
Some(span),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Create a simple custom error",
|
|
||||||
example: r#"error make {msg: "my custom error message"}"#,
|
|
||||||
result: Some(Value::Error {
|
|
||||||
error: Box::new(ShellError::GenericError(
|
|
||||||
"my custom error message".to_string(),
|
|
||||||
"".to_string(),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
)),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Create a more complex custom error",
|
|
||||||
example: r#"error make {
|
|
||||||
msg: "my custom error message"
|
|
||||||
label: {
|
|
||||||
text: "my custom label text" # not mandatory unless $.label exists
|
|
||||||
start: 123 # not mandatory unless $.label.end is set
|
|
||||||
end: 456 # not mandatory unless $.label.start is set
|
|
||||||
}
|
|
||||||
}"#,
|
|
||||||
result: Some(Value::Error {
|
|
||||||
error: Box::new(ShellError::GenericError(
|
|
||||||
"my custom error message".to_string(),
|
|
||||||
"my custom label text".to_string(),
|
|
||||||
Some(Span::new(123, 456)),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
)),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description:
|
|
||||||
"Create a custom error for a custom command that shows the span of the argument",
|
|
||||||
example: r#"def foo [x] {
|
|
||||||
let span = (metadata $x).span;
|
|
||||||
error make {
|
|
||||||
msg: "this is fishy"
|
|
||||||
label: {
|
|
||||||
text: "fish right here"
|
|
||||||
start: $span.start
|
|
||||||
end: $span.end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}"#,
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
|
|
||||||
if let Value::Record { span, .. } = &value {
|
|
||||||
let msg = value.get_data_by_key("msg");
|
|
||||||
let label = value.get_data_by_key("label");
|
|
||||||
|
|
||||||
match (msg, &label) {
|
|
||||||
(Some(Value::String { val: message, .. }), Some(label)) => {
|
|
||||||
let label_start = label.get_data_by_key("start");
|
|
||||||
let label_end = label.get_data_by_key("end");
|
|
||||||
let label_text = label.get_data_by_key("text");
|
|
||||||
|
|
||||||
let label_span = match label.span() {
|
|
||||||
Ok(lspan) => Some(lspan),
|
|
||||||
Err(_) => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
match (label_start, label_end, label_text) {
|
|
||||||
(
|
|
||||||
Some(Value::Int { val: start, .. }),
|
|
||||||
Some(Value::Int { val: end, .. }),
|
|
||||||
Some(Value::String {
|
|
||||||
val: label_text, ..
|
|
||||||
}),
|
|
||||||
) => {
|
|
||||||
if start > end {
|
|
||||||
Some(ShellError::GenericError(
|
|
||||||
"invalid error format.".into(),
|
|
||||||
"`$.label.start` should be smaller than `$.label.end`".into(),
|
|
||||||
label_span,
|
|
||||||
Some(format!("{} > {}", start, end)),
|
|
||||||
Vec::new(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Some(ShellError::GenericError(
|
|
||||||
message,
|
|
||||||
label_text,
|
|
||||||
Some(Span::new(start as usize, end as usize)),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Some(Value::String {
|
|
||||||
val: label_text, ..
|
|
||||||
}),
|
|
||||||
) => Some(ShellError::GenericError(
|
|
||||||
message,
|
|
||||||
label_text,
|
|
||||||
throw_span,
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
)),
|
|
||||||
(_, _, None) => Some(ShellError::GenericError(
|
|
||||||
"Unable to parse error format.".into(),
|
|
||||||
"missing required member `$.label.text`".into(),
|
|
||||||
label_span,
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
)),
|
|
||||||
(Some(Value::Int { .. }), None, _) => Some(ShellError::GenericError(
|
|
||||||
"Unable to parse error format.".into(),
|
|
||||||
"missing required member `$.label.end`".into(),
|
|
||||||
label_span,
|
|
||||||
Some("required because `$.label.start` is set".to_string()),
|
|
||||||
Vec::new(),
|
|
||||||
)),
|
|
||||||
(None, Some(Value::Int { .. }), _) => Some(ShellError::GenericError(
|
|
||||||
"Unable to parse error format.".into(),
|
|
||||||
"missing required member `$.label.start`".into(),
|
|
||||||
label_span,
|
|
||||||
Some("required because `$.label.end` is set".to_string()),
|
|
||||||
Vec::new(),
|
|
||||||
)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Some(Value::String { val: message, .. }), None) => Some(ShellError::GenericError(
|
|
||||||
message,
|
|
||||||
"originates from here".to_string(),
|
|
||||||
throw_span,
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
)),
|
|
||||||
(None, _) => Some(ShellError::GenericError(
|
|
||||||
"Unable to parse error format.".into(),
|
|
||||||
"missing required member `$.msg`".into(),
|
|
||||||
Some(*span),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,75 +0,0 @@
|
|||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ExportModule;
|
|
||||||
|
|
||||||
impl Command for ExportModule {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"export module"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Export a custom module from a module."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
|
||||||
Signature::build("export module")
|
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
|
||||||
.allow_variants_without_examples(true)
|
|
||||||
.required("module", SyntaxShape::String, "module name or module path")
|
|
||||||
.optional(
|
|
||||||
"block",
|
|
||||||
SyntaxShape::Block,
|
|
||||||
"body of the module if 'module' parameter is not a path",
|
|
||||||
)
|
|
||||||
.category(Category::Core)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
|
||||||
r#"This command is a parser keyword. For details, check:
|
|
||||||
https://www.nushell.sh/book/thinking_in_nu.html"#
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_parser_keyword(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
_engine_state: &EngineState,
|
|
||||||
_stack: &mut Stack,
|
|
||||||
_call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
Ok(PipelineData::empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Define a custom command in a submodule of a module and call it",
|
|
||||||
example: r#"module spam {
|
|
||||||
export module eggs {
|
|
||||||
export def foo [] { "foo" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
use spam eggs
|
|
||||||
eggs foo"#,
|
|
||||||
result: Some(Value::test_string("foo")),
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(ExportModule {})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,64 +0,0 @@
|
|||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Hide;
|
|
||||||
|
|
||||||
impl Command for Hide {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"hide"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
|
||||||
Signature::build("hide")
|
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
|
||||||
.required("module", SyntaxShape::String, "Module or module file")
|
|
||||||
.optional(
|
|
||||||
"members",
|
|
||||||
SyntaxShape::Any,
|
|
||||||
"Which members of the module to import",
|
|
||||||
)
|
|
||||||
.category(Category::Core)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Hide definitions in the current scope."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
|
||||||
r#"Definitions are hidden by priority: First aliases, then custom commands.
|
|
||||||
|
|
||||||
This command is a parser keyword. For details, check:
|
|
||||||
https://www.nushell.sh/book/thinking_in_nu.html"#
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_parser_keyword(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
_engine_state: &EngineState,
|
|
||||||
_stack: &mut Stack,
|
|
||||||
_call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
Ok(PipelineData::empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Hide the alias just defined",
|
|
||||||
example: r#"alias lll = ls -l; hide lll"#,
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Hide a custom command",
|
|
||||||
example: r#"def say-hi [] { echo 'Hi!' }; hide say-hi"#,
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,158 +0,0 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use nu_engine::{eval_block, CallExt};
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, IntoPipelineData, LazyRecord, PipelineData, ShellError, Signature, Span,
|
|
||||||
SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct LazyMake;
|
|
||||||
|
|
||||||
impl Command for LazyMake {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"lazy make"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("lazy make")
|
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Record(vec![]))])
|
|
||||||
.required_named(
|
|
||||||
"columns",
|
|
||||||
SyntaxShape::List(Box::new(SyntaxShape::String)),
|
|
||||||
"Closure that gets called when the LazyRecord needs to list the available column names",
|
|
||||||
Some('c')
|
|
||||||
)
|
|
||||||
.required_named(
|
|
||||||
"get-value",
|
|
||||||
SyntaxShape::Closure(Some(vec![SyntaxShape::String])),
|
|
||||||
"Closure to call when a value needs to be produced on demand",
|
|
||||||
Some('g')
|
|
||||||
)
|
|
||||||
.category(Category::Core)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Create a lazy record."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
|
||||||
"Lazy records are special records that only evaluate their values once the property is requested.
|
|
||||||
For example, when printing a lazy record, all of its fields will be collected. But when accessing
|
|
||||||
a specific property, only it will be evaluated.
|
|
||||||
|
|
||||||
Note that this is unrelated to the lazyframes feature bundled with dataframes."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["deferred", "record", "procedural"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let span = call.head;
|
|
||||||
let columns: Vec<String> = call
|
|
||||||
.get_flag(engine_state, stack, "columns")?
|
|
||||||
.expect("required flag");
|
|
||||||
let get_value: Closure = call
|
|
||||||
.get_flag(engine_state, stack, "get-value")?
|
|
||||||
.expect("required flag");
|
|
||||||
|
|
||||||
Ok(Value::LazyRecord {
|
|
||||||
val: Box::new(NuLazyRecord {
|
|
||||||
engine_state: engine_state.clone(),
|
|
||||||
stack: Arc::new(Mutex::new(stack.clone())),
|
|
||||||
columns,
|
|
||||||
get_value,
|
|
||||||
span,
|
|
||||||
}),
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
.into_pipeline_data())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
// TODO: Figure out how to "test" these examples, or leave results as None
|
|
||||||
Example {
|
|
||||||
description: "Create a lazy record",
|
|
||||||
example: r#"lazy make --columns ["haskell", "futures", "nushell"] --get-value { |lazything| $lazything + "!" }"#,
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Test the laziness of lazy records",
|
|
||||||
example: r#"lazy make -c ["hello"] -g { |key| print $"getting ($key)!"; $key | str upcase }"#,
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct NuLazyRecord {
|
|
||||||
engine_state: EngineState,
|
|
||||||
stack: Arc<Mutex<Stack>>,
|
|
||||||
columns: Vec<String>,
|
|
||||||
get_value: Closure,
|
|
||||||
span: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for NuLazyRecord {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("NuLazyRecord").finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> LazyRecord<'a> for NuLazyRecord {
|
|
||||||
fn column_names(&'a self) -> Vec<&'a str> {
|
|
||||||
self.columns.iter().map(|column| column.as_str()).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_column_value(&self, column: &str) -> Result<Value, ShellError> {
|
|
||||||
let block = self.engine_state.get_block(self.get_value.block_id);
|
|
||||||
let mut stack = self.stack.lock().expect("lock must not be poisoned");
|
|
||||||
let column_value = Value::String {
|
|
||||||
val: column.into(),
|
|
||||||
span: self.span,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
|
||||||
if let Some(var_id) = &var.var_id {
|
|
||||||
stack.add_var(*var_id, column_value.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let pipeline_result = eval_block(
|
|
||||||
&self.engine_state,
|
|
||||||
&mut stack,
|
|
||||||
block,
|
|
||||||
PipelineData::Value(column_value, None),
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
pipeline_result.map(|data| match data {
|
|
||||||
PipelineData::Value(value, ..) => value,
|
|
||||||
// TODO: Proper error handling.
|
|
||||||
_ => Value::Nothing { span: self.span },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn span(&self) -> Span {
|
|
||||||
self.span
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clone_value(&self, span: Span) -> Value {
|
|
||||||
Value::LazyRecord {
|
|
||||||
val: Box::new((*self).clone()),
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,124 +0,0 @@
|
|||||||
use nu_engine::{eval_block, eval_expression_with_input, CallExt};
|
|
||||||
use nu_protocol::ast::{Call, Expr, Expression};
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Matcher, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Match;
|
|
||||||
|
|
||||||
impl Command for Match {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"match"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Conditionally run a block on a matched value."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
|
||||||
Signature::build("match")
|
|
||||||
.input_output_types(vec![(Type::Any, Type::Any)])
|
|
||||||
.required("value", SyntaxShape::Any, "value to check")
|
|
||||||
.required(
|
|
||||||
"match_block",
|
|
||||||
SyntaxShape::MatchBlock,
|
|
||||||
"block to run if check succeeds",
|
|
||||||
)
|
|
||||||
.category(Category::Core)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let value: Value = call.req(engine_state, stack, 0)?;
|
|
||||||
let block = call.positional_nth(1);
|
|
||||||
|
|
||||||
if let Some(Expression {
|
|
||||||
expr: Expr::MatchBlock(matches),
|
|
||||||
..
|
|
||||||
}) = block
|
|
||||||
{
|
|
||||||
for match_ in matches {
|
|
||||||
let mut match_variables = vec![];
|
|
||||||
if match_.0.match_value(&value, &mut match_variables) {
|
|
||||||
// This case does match, go ahead and return the evaluated expression
|
|
||||||
for match_variable in match_variables {
|
|
||||||
stack.add_var(match_variable.0, match_variable.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(block_id) = match_.1.as_block() {
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
return eval_block(
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
block,
|
|
||||||
input,
|
|
||||||
call.redirect_stdout,
|
|
||||||
call.redirect_stderr,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return eval_expression_with_input(
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
&match_.1,
|
|
||||||
input,
|
|
||||||
call.redirect_stdout,
|
|
||||||
call.redirect_stderr,
|
|
||||||
)
|
|
||||||
.map(|x| x.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(PipelineData::Empty)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Match on a value in range",
|
|
||||||
example: "match 3 { 1..10 => 'yes!' }",
|
|
||||||
result: Some(Value::test_string("yes!")),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Match on a field in a record",
|
|
||||||
example: "match {a: 100} { {a: $my_value} => { $my_value } }",
|
|
||||||
result: Some(Value::test_int(100)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Match with a catch-all",
|
|
||||||
example: "match 3 { 1 => { 'yes!' }, _ => { 'no!' } }",
|
|
||||||
result: Some(Value::test_string("no!")),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Match against a list",
|
|
||||||
example: "match [1, 2, 3] { [$a, $b, $c] => { $a + $b + $c }, _ => 0 }",
|
|
||||||
result: Some(Value::test_int(6)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Match against pipeline input",
|
|
||||||
example: "{a: {b: 3}} | match $in {{a: { $b }} => ($b + 10) }",
|
|
||||||
result: Some(Value::test_int(13)),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(Match {})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,62 +0,0 @@
|
|||||||
use nu_engine::scope::ScopeData;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ScopeAliases;
|
|
||||||
|
|
||||||
impl Command for ScopeAliases {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"scope aliases"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("scope aliases")
|
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Any)])
|
|
||||||
.allow_variants_without_examples(true)
|
|
||||||
.category(Category::Filters)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Output info on the aliases in the current scope."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let span = call.head;
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
|
||||||
|
|
||||||
let mut scope_data = ScopeData::new(engine_state, stack);
|
|
||||||
scope_data.populate_all();
|
|
||||||
|
|
||||||
Ok(scope_data.collect_aliases(span).into_pipeline_data(ctrlc))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Show the aliases in the current scope",
|
|
||||||
example: "scope aliases",
|
|
||||||
result: None,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(ScopeAliases {})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,50 +0,0 @@
|
|||||||
use nu_engine::get_full_help;
|
|
||||||
use nu_protocol::{
|
|
||||||
ast::Call,
|
|
||||||
engine::{Command, EngineState, Stack},
|
|
||||||
Category, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Scope;
|
|
||||||
|
|
||||||
impl Command for Scope {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"scope"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("scope")
|
|
||||||
.category(Category::Core)
|
|
||||||
.input_output_types(vec![(Type::Nothing, Type::String)])
|
|
||||||
.allow_variants_without_examples(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Commands for getting info about what is in scope."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_parser_keyword(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
Ok(Value::String {
|
|
||||||
val: get_full_help(
|
|
||||||
&Scope.signature(),
|
|
||||||
&[],
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
self.is_parser_keyword(),
|
|
||||||
),
|
|
||||||
span: call.head,
|
|
||||||
}
|
|
||||||
.into_pipeline_data())
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,62 +0,0 @@
|
|||||||
use nu_engine::scope::ScopeData;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ScopeCommands;
|
|
||||||
|
|
||||||
impl Command for ScopeCommands {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"scope commands"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("scope commands")
|
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Any)])
|
|
||||||
.allow_variants_without_examples(true)
|
|
||||||
.category(Category::Filters)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Output info on the commands in the current scope."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let span = call.head;
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
|
||||||
|
|
||||||
let mut scope_data = ScopeData::new(engine_state, stack);
|
|
||||||
scope_data.populate_all();
|
|
||||||
|
|
||||||
Ok(scope_data.collect_commands(span).into_pipeline_data(ctrlc))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Show the commands in the current scope",
|
|
||||||
example: "scope commands",
|
|
||||||
result: None,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(ScopeCommands {})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,59 +0,0 @@
|
|||||||
use nu_engine::scope::ScopeData;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ScopeEngineStats;
|
|
||||||
|
|
||||||
impl Command for ScopeEngineStats {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"scope engine-stats"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("scope engine-stats")
|
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Any)])
|
|
||||||
.allow_variants_without_examples(true)
|
|
||||||
.category(Category::Filters)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Output stats on the engine in the current state."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let span = call.head;
|
|
||||||
|
|
||||||
let mut scope_data = ScopeData::new(engine_state, stack);
|
|
||||||
scope_data.populate_all();
|
|
||||||
|
|
||||||
Ok(scope_data.collect_engine_state(span).into_pipeline_data())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Show the stats on the current engine state",
|
|
||||||
example: "scope engine-stats",
|
|
||||||
result: None,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(ScopeEngineStats {})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,13 +0,0 @@
|
|||||||
mod aliases;
|
|
||||||
mod command;
|
|
||||||
mod commands;
|
|
||||||
mod engine_stats;
|
|
||||||
mod modules;
|
|
||||||
mod variables;
|
|
||||||
|
|
||||||
pub use aliases::*;
|
|
||||||
pub use command::*;
|
|
||||||
pub use commands::*;
|
|
||||||
pub use engine_stats::*;
|
|
||||||
pub use modules::*;
|
|
||||||
pub use variables::*;
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user