mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-22 07:11:03 +00:00
Compare commits
No commits in common. "main" and "v25.5.28" have entirely different histories.
1537 changed files with 24386 additions and 65760 deletions
|
|
@ -1,5 +1,5 @@
|
|||
[env]
|
||||
MACOSX_DEPLOYMENT_TARGET = "10.12"
|
||||
MACOSX_DEPLOYMENT_TARGET = "10.11"
|
||||
JEMALLOC_SYS_WITH_LG_PAGE = "16"
|
||||
|
||||
# environment variable for tikv-jemalloc-sys
|
||||
|
|
|
|||
6
.github/DISCUSSION_TEMPLATE/1-q-a.yml
vendored
6
.github/DISCUSSION_TEMPLATE/1-q-a.yml
vendored
|
|
@ -22,10 +22,10 @@ body:
|
|||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: env
|
||||
id: debug
|
||||
attributes:
|
||||
label: "`ya env` output"
|
||||
description: Run `ya env` and paste the environment information here.
|
||||
label: "`yazi --debug` output"
|
||||
description: Please run `yazi --debug` and paste the debug information here.
|
||||
render: Shell
|
||||
validations:
|
||||
required: true
|
||||
|
|
|
|||
8
.github/ISSUE_TEMPLATE/bug.yml
vendored
8
.github/ISSUE_TEMPLATE/bug.yml
vendored
|
|
@ -26,10 +26,10 @@ body:
|
|||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: env
|
||||
id: debug
|
||||
attributes:
|
||||
label: "`ya env` output"
|
||||
description: Run `ya env` and paste the environment information here.
|
||||
label: "`yazi --debug` output"
|
||||
description: Please run `yazi --debug` and paste the debug information here.
|
||||
render: Shell
|
||||
validations:
|
||||
required: true
|
||||
|
|
@ -65,6 +65,6 @@ body:
|
|||
options:
|
||||
- label: I tried the [latest nightly build](https://yazi-rs.github.io/docs/installation#binaries), and the issue is still reproducible
|
||||
required: true
|
||||
- label: I updated the environment information (`ya env`) field to the nightly that I tried
|
||||
- label: I updated the debug information (`yazi --debug`) input box to the nightly that I tried
|
||||
required: true
|
||||
- label: I can reproduce it after disabling all custom configs/plugins (`mv ~/.config/yazi ~/.config/yazi-backup`)
|
||||
|
|
|
|||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,8 +1,5 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 🚧 Build Issues
|
||||
url: https://github.com/sxyazi/yazi/discussions/new?category=3-build-issues
|
||||
about: If you have issues building Yazi from source code
|
||||
- name: 📝 Documentation Improvement
|
||||
url: https://github.com/yazi-rs/yazi-rs.github.io
|
||||
about: If you'd like to help improve the documentation
|
||||
|
|
|
|||
6
.github/ISSUE_TEMPLATE/feature.yml
vendored
6
.github/ISSUE_TEMPLATE/feature.yml
vendored
|
|
@ -4,10 +4,10 @@ labels: [feature]
|
|||
assignees: []
|
||||
body:
|
||||
- type: textarea
|
||||
id: env
|
||||
id: debug
|
||||
attributes:
|
||||
label: "`ya env` output"
|
||||
description: Run `ya env` and paste the environment information here.
|
||||
label: "`yazi --debug` output"
|
||||
description: Please run `yazi --debug` and paste the debug information here.
|
||||
render: Shell
|
||||
validations:
|
||||
required: true
|
||||
|
|
|
|||
15
.github/dependabot.yml
vendored
15
.github/dependabot.yml
vendored
|
|
@ -1,15 +0,0 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: "ci"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/scripts/validate-form"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: "ci"
|
||||
7
.github/pull_request_template.md
vendored
7
.github/pull_request_template.md
vendored
|
|
@ -15,10 +15,3 @@ A clear and concise description of the rationale of the changes, to help our rev
|
|||
|
||||
If it has already been detailed in the associated issue, please skip this section.
|
||||
-->
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have read [CONTRIBUTING.md](https://github.com/sxyazi/yazi/blob/main/CONTRIBUTING.md)
|
||||
- [ ] I confirm this PR follows the [AI Policy](https://github.com/sxyazi/yazi/blob/main/CONTRIBUTING.md#ai-policy)
|
||||
|
||||
<!-- AI bots are not allowed to open PRs in this repository. All PRs must be made by humans and comply with the AI policy. -->
|
||||
|
|
|
|||
7
.github/workflows/cachix.yml
vendored
7
.github/workflows/cachix.yml
vendored
|
|
@ -4,9 +4,6 @@ on:
|
|||
push:
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish Flake
|
||||
|
|
@ -15,13 +12,13 @@ jobs:
|
|||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v31
|
||||
|
||||
- name: Authenticate with Cachix
|
||||
uses: cachix/cachix-action@v17
|
||||
uses: cachix/cachix-action@v16
|
||||
with:
|
||||
name: yazi
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
|
|
|||
17
.github/workflows/check.yml
vendored
17
.github/workflows/check.yml
vendored
|
|
@ -10,14 +10,11 @@ env:
|
|||
SCCACHE_GHA_ENABLED: true
|
||||
RUSTC_WRAPPER: sccache
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
run: |
|
||||
|
|
@ -25,7 +22,7 @@ jobs:
|
|||
rustup component add clippy
|
||||
|
||||
- name: Setup sccache
|
||||
uses: mozilla-actions/sccache-action@v0.0.10
|
||||
uses: mozilla-actions/sccache-action@v0.0.9
|
||||
|
||||
- name: Clippy
|
||||
run: cargo clippy --all
|
||||
|
|
@ -34,7 +31,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
run: |
|
||||
|
|
@ -42,16 +39,16 @@ jobs:
|
|||
rustup component add rustfmt --toolchain nightly
|
||||
|
||||
- name: Setup sccache
|
||||
uses: mozilla-actions/sccache-action@v0.0.10
|
||||
uses: mozilla-actions/sccache-action@v0.0.9
|
||||
|
||||
- name: Rustfmt
|
||||
run: find . -name '*.rs' -not -path './target/*' -exec rustfmt +nightly --check {} +
|
||||
run: rustfmt +nightly --check **/*.rs
|
||||
|
||||
stylua:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: JohnnyMorganz/stylua-action@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: JohnnyMorganz/stylua-action@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: latest
|
||||
|
|
|
|||
88
.github/workflows/draft.yml
vendored
88
.github/workflows/draft.yml
vendored
|
|
@ -9,9 +9,6 @@ on:
|
|||
env:
|
||||
SCCACHE_GHA_ENABLED: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-unix:
|
||||
strategy:
|
||||
|
|
@ -42,29 +39,26 @@ jobs:
|
|||
CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER: riscv64-linux-gnu-gcc
|
||||
CARGO_TARGET_SPARC64_UNKNOWN_LINUX_GNU_LINKER: sparc64-linux-gnu-gcc
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install gcc
|
||||
if: matrix.gcc != ''
|
||||
run: sudo apt update && sudo apt install -yq ${{ matrix.gcc }}
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
- name: Setup Rust toolchain
|
||||
run: rustup toolchain install stable --profile minimal --target ${{ matrix.target }}
|
||||
|
||||
- name: Setup sccache
|
||||
uses: mozilla-actions/sccache-action@v0.0.10
|
||||
uses: mozilla-actions/sccache-action@v0.0.9
|
||||
|
||||
- name: Build
|
||||
run: ./scripts/build.sh ${{ matrix.target }}
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.target }}
|
||||
path: |
|
||||
yazi-${{ matrix.target }}.zip
|
||||
yazi-${{ matrix.target }}.deb
|
||||
name: yazi-${{ matrix.target }}.zip
|
||||
path: yazi-${{ matrix.target }}.zip
|
||||
|
||||
build-windows:
|
||||
strategy:
|
||||
|
|
@ -81,14 +75,13 @@ jobs:
|
|||
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER: lld-link.exe
|
||||
CARGO_TARGET_AARCH64_PC_WINDOWS_MSVC_LINKER: lld-link.exe
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
- name: Setup Rust toolchain
|
||||
run: rustup toolchain install stable --profile minimal --target ${{ matrix.target }}
|
||||
|
||||
- name: Setup sccache
|
||||
uses: mozilla-actions/sccache-action@v0.0.10
|
||||
uses: mozilla-actions/sccache-action@v0.0.9
|
||||
|
||||
- name: Build
|
||||
run: cargo build --profile release-windows --locked --target ${{ matrix.target }}
|
||||
|
|
@ -97,18 +90,17 @@ jobs:
|
|||
env:
|
||||
TARGET_NAME: yazi-${{ matrix.target }}
|
||||
run: |
|
||||
New-Item -ItemType Directory -Path "${env:TARGET_NAME}\completions"
|
||||
New-Item -ItemType Directory -Path ${env:TARGET_NAME}
|
||||
Copy-Item -Path "target\${{ matrix.target }}\release-windows\ya.exe" -Destination ${env:TARGET_NAME}
|
||||
Copy-Item -Path "target\${{ matrix.target }}\release-windows\yazi.exe" -Destination ${env:TARGET_NAME}
|
||||
Copy-Item -Path "yazi-cli\completions\*" -Destination "${env:TARGET_NAME}\completions"
|
||||
Copy-Item -Path "yazi-boot\completions\*" -Destination "${env:TARGET_NAME}\completions"
|
||||
Copy-Item -Path "yazi-boot\completions" -Destination ${env:TARGET_NAME} -Recurse
|
||||
Copy-Item -Path "README.md", "LICENSE" -Destination ${env:TARGET_NAME}
|
||||
Compress-Archive -Path ${env:TARGET_NAME} -DestinationPath "${env:TARGET_NAME}.zip"
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.target }}
|
||||
name: yazi-${{ matrix.target }}.zip
|
||||
path: yazi-${{ matrix.target }}.zip
|
||||
|
||||
build-musl:
|
||||
|
|
@ -117,29 +109,28 @@ jobs:
|
|||
matrix:
|
||||
include:
|
||||
- target: x86_64-unknown-linux-musl
|
||||
image: rust-musl-cross:x86_64-musl
|
||||
- target: aarch64-unknown-linux-musl
|
||||
image: rust-musl-cross:aarch64-musl
|
||||
container:
|
||||
image: docker://ghcr.io/cross-rs/${{ matrix.target }}:edge
|
||||
image: docker://ghcr.io/rust-cross/${{ matrix.image }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
- name: Add musl target
|
||||
run: rustup target add ${{ matrix.target }}
|
||||
|
||||
- name: Setup sccache
|
||||
uses: mozilla-actions/sccache-action@v0.0.10
|
||||
uses: mozilla-actions/sccache-action@v0.0.9
|
||||
|
||||
- name: Build
|
||||
run: ./scripts/build.sh ${{ matrix.target }}
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.target }}
|
||||
path: |
|
||||
yazi-${{ matrix.target }}.zip
|
||||
yazi-${{ matrix.target }}.deb
|
||||
name: yazi-${{ matrix.target }}.zip
|
||||
path: yazi-${{ matrix.target }}.zip
|
||||
|
||||
build-snap:
|
||||
strategy:
|
||||
|
|
@ -151,12 +142,12 @@ jobs:
|
|||
arch: arm64
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup LXD
|
||||
uses: canonical/setup-lxd@v1
|
||||
uses: canonical/setup-lxd@v0.1.2
|
||||
|
||||
- name: Setup snapcraft
|
||||
run: sudo snap install --classic snapcraft
|
||||
|
|
@ -168,18 +159,18 @@ jobs:
|
|||
run: mv yazi_*.snap yazi-${{ matrix.arch }}.snap
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: snap-${{ matrix.arch }}
|
||||
name: yazi-${{ matrix.arch }}.snap
|
||||
path: yazi-${{ matrix.arch }}.snap
|
||||
|
||||
snap:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-snap]
|
||||
steps:
|
||||
- uses: actions/download-artifact@v8
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: snap-*
|
||||
pattern: yazi-*.snap
|
||||
merge-multiple: true
|
||||
|
||||
- name: Setup snapcraft
|
||||
|
|
@ -190,14 +181,14 @@ jobs:
|
|||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }}
|
||||
run: |
|
||||
parallel 'snapcraft upload -v --release latest/candidate {}' ::: yazi-*.snap || true
|
||||
parallel 'snapcraft push -v --release latest/candidate {}' ::: yazi-*.snap
|
||||
|
||||
- name: Push snap to edge channel
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }}
|
||||
run: |
|
||||
parallel 'snapcraft upload -v --release latest/edge {}' ::: yazi-*.snap || true
|
||||
parallel 'snapcraft push -v --release latest/edge {}' ::: yazi-*.snap
|
||||
|
||||
draft:
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
|
|
@ -206,17 +197,16 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
needs: [build-unix, build-windows, build-musl, build-snap]
|
||||
steps:
|
||||
- uses: actions/download-artifact@v8
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
|
||||
- name: Draft
|
||||
uses: softprops/action-gh-release@v3
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
draft: true
|
||||
files: |
|
||||
yazi-*.zip
|
||||
yazi-*.deb
|
||||
yazi-*.snap
|
||||
generate_release_notes: true
|
||||
|
||||
|
|
@ -231,12 +221,11 @@ jobs:
|
|||
echo 'NIGHTLY_BODY<<EOF' >> $GITHUB_ENV
|
||||
echo "From commit: ${GITHUB_SHA:0:8}" >> $GITHUB_ENV
|
||||
echo "Generated on: $(date -u +"%Y-%m-%d %H:%M") UTC" >> $GITHUB_ENV
|
||||
echo "Nightly changelog: https://github.com/sxyazi/yazi/blob/main/CHANGELOG.md#unreleased" >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@v8
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
|
||||
|
|
@ -247,13 +236,12 @@ jobs:
|
|||
git tag --force nightly && git push --force origin tag nightly
|
||||
|
||||
- name: Nightly
|
||||
uses: softprops/action-gh-release@v3
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: nightly
|
||||
prerelease: true
|
||||
files: |
|
||||
yazi-*.zip
|
||||
yazi-*.deb
|
||||
yazi-*.snap
|
||||
name: Nightly Build
|
||||
body: ${{ env.NIGHTLY_BODY }}
|
||||
|
|
|
|||
2
.github/workflows/lock.yml
vendored
2
.github/workflows/lock.yml
vendored
|
|
@ -16,7 +16,7 @@ jobs:
|
|||
pull-requests: write
|
||||
discussions: write
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v6
|
||||
- uses: dessant/lock-threads@v5
|
||||
with:
|
||||
issue-inactive-days: "30"
|
||||
issue-comment: >
|
||||
|
|
|
|||
12
.github/workflows/publish.yml
vendored
12
.github/workflows/publish.yml
vendored
|
|
@ -4,14 +4,11 @@ on:
|
|||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
winget:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/download-artifact@v8
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
|
||||
|
|
@ -22,13 +19,6 @@ jobs:
|
|||
installers-regex: 'yazi-(x86_64|aarch64)-pc-windows-msvc\.zip$'
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
||||
|
||||
snapcraft:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
merge-multiple: true
|
||||
|
||||
- name: Promote snap to stable
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }}
|
||||
|
|
|
|||
7
.github/workflows/test.yml
vendored
7
.github/workflows/test.yml
vendored
|
|
@ -11,9 +11,6 @@ env:
|
|||
RUSTC_WRAPPER: sccache
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
|
|
@ -21,13 +18,13 @@ jobs:
|
|||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
run: rustup toolchain install stable --profile minimal
|
||||
|
||||
- name: Setup sccache
|
||||
uses: mozilla-actions/sccache-action@v0.0.10
|
||||
uses: mozilla-actions/sccache-action@v0.0.9
|
||||
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
name: Validate Issue
|
||||
name: Validate Form
|
||||
|
||||
on:
|
||||
issues:
|
||||
|
|
@ -12,20 +12,20 @@ jobs:
|
|||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-js: 20
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
cd scripts/validate-form
|
||||
npm ci
|
||||
|
||||
- name: Validate Issue
|
||||
uses: actions/github-script@v9
|
||||
- name: Validate Form
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const script = require('./scripts/validate-form/main.js')
|
||||
36
.github/workflows/validate-pr.yml
vendored
36
.github/workflows/validate-pr.yml
vendored
|
|
@ -1,36 +0,0 @@
|
|||
name: Validate PR
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
check-list:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ github.event.repository.default_branch }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
cd scripts/validate-form
|
||||
npm ci
|
||||
|
||||
- name: Validate PR
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const script = require('./scripts/validate-form/main.js')
|
||||
await script({github, context, core})
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
|
||||
"runtime.version": "Lua 5.5",
|
||||
"runtime.special": {
|
||||
"fail": "error"
|
||||
},
|
||||
"workspace.library": ["~/.config/yazi/plugins/types.yazi/"],
|
||||
"diagnostics.disable": ["redefined-local"]
|
||||
}
|
||||
2
.styluaignore
Normal file
2
.styluaignore
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# this file caused some issues with the build system
|
||||
yazi-plugin/preset/plugins/mime.lua
|
||||
1785
CHANGELOG.md
1785
CHANGELOG.md
File diff suppressed because it is too large
Load diff
|
|
@ -11,7 +11,6 @@ This guide will help you understand how to contribute to the project.
|
|||
3. [Development Setup](#development-setup)
|
||||
4. [How to Contribute](#how-to-contribute)
|
||||
5. [Pull Requests](#pull-requests)
|
||||
6. [AI Policy](#ai-policy)
|
||||
|
||||
## Getting Started
|
||||
|
||||
|
|
@ -165,10 +164,3 @@ We want you to succeed, and it can be discouraging to find that a lot of re-work
|
|||
- Your code passes all tests and lints.
|
||||
- Your pull request description clearly explains the changes and why they are needed.
|
||||
4. Address any review comments. Make sure to push updates to the same branch on your fork.
|
||||
|
||||
## AI Policy
|
||||
|
||||
1. All issue, PR, and discussion descriptions must be written by humans, not AI.
|
||||
2. Any use of AI must be disclosed. You must declare which model you used and the extent of AI assistance.
|
||||
3. Any AI-generated code must be reviewed, tested, and simplified by a human before publishing. This requires you to fully understand how it interacts with the greater system without AI assistance.
|
||||
4. Any AI tools used must explicitly state they do not assert copyright over the work.
|
||||
|
|
|
|||
4084
Cargo.lock
generated
4084
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
99
Cargo.toml
99
Cargo.toml
|
|
@ -1,20 +1,8 @@
|
|||
[workspace]
|
||||
resolver = "3"
|
||||
resolver = "2"
|
||||
members = [ "yazi-*" ]
|
||||
default-members = [ "yazi-fm", "yazi-cli" ]
|
||||
|
||||
[workspace.package]
|
||||
edition = "2024"
|
||||
version = "26.5.6"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
homepage = "https://yazi-rs.github.io"
|
||||
repository = "https://github.com/sxyazi/yazi"
|
||||
rust-version = "1.95.0"
|
||||
|
||||
[profile.dev]
|
||||
debug = "line-tables-only"
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
lto = true
|
||||
|
|
@ -31,67 +19,36 @@ codegen-units = 256
|
|||
incremental = true
|
||||
lto = false
|
||||
|
||||
[profile.dev.package."*"]
|
||||
debug = false
|
||||
|
||||
[workspace.dependencies]
|
||||
ansi-to-tui = "8.0.1"
|
||||
anyhow = "1.0.104"
|
||||
arc-swap = { version = "1.9.2", features = [ "serde" ] }
|
||||
ansi-to-tui = "7.0.0"
|
||||
anyhow = "1.0.98"
|
||||
base64 = "0.22.1"
|
||||
bitflags = { version = "2.13.1", features = [ "serde" ] }
|
||||
chrono = "0.4.45"
|
||||
clap = { version = "4.6.2", features = [ "derive" ] }
|
||||
compact_str = { version = "0.10.0", features = [ "serde" ] }
|
||||
bitflags = "2.9.1"
|
||||
clap = { version = "4.5.39", features = [ "derive" ] }
|
||||
core-foundation-sys = "0.8.7"
|
||||
data-encoding = "2.11.0"
|
||||
crossterm = { version = "0.29.0", features = [ "event-stream" ] }
|
||||
dirs = "6.0.0"
|
||||
dyn-clone = "1.0.20"
|
||||
either = { version = "1.16.0" }
|
||||
foldhash = "0.2.0"
|
||||
futures = "0.3.33"
|
||||
globset = "0.4.19"
|
||||
hashbrown = { version = "0.17.1", features = [ "serde" ] }
|
||||
image = { version = "0.25.10", default-features = false, features = [ "avif", "bmp", "dds", "exr", "ff", "gif", "hdr", "ico", "jpeg", "png", "pnm", "qoi", "tga", "tiff", "webp" ] }
|
||||
indexmap = { version = "2.14.0", features = [ "serde" ] }
|
||||
inventory = "0.3.24"
|
||||
libc = "0.2.186"
|
||||
lru = "0.18.1"
|
||||
mlua = { version = "0.12.0", features = [ "anyhow", "async", "error-send", "lua55", "macros", "serde" ] }
|
||||
objc2 = "0.6.4"
|
||||
ordered-float = { version = "5.3.0", features = [ "serde" ] }
|
||||
parking_lot = "0.12.5"
|
||||
foldhash = "0.1.5"
|
||||
futures = "0.3.31"
|
||||
globset = "0.4.16"
|
||||
indexmap = { version = "2.9.0", features = [ "serde" ] }
|
||||
libc = "0.2.172"
|
||||
lru = "0.14.0"
|
||||
mlua = { version = "0.10.5", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serialize" ] }
|
||||
objc = "0.2.7"
|
||||
parking_lot = "0.12.3"
|
||||
paste = "1.0.15"
|
||||
percent-encoding = "2.3.2"
|
||||
rand = { version = "0.10.2", default-features = false, features = [ "std", "sys_rng" ] }
|
||||
ratatui-core = { version = "0.1.2", default-features = false, features = [ "std", "layout-cache", "serde", "underline-color" ] }
|
||||
ratatui-widgets = { version = "0.3.2", default-features = false, features = [ "std", "unstable-rendered-line-info" ] }
|
||||
regex = "1.13.1"
|
||||
russh = { version = "0.62.2", default-features = false, features = [ "ring", "rsa" ] }
|
||||
ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info" ] }
|
||||
regex = "1.11.1"
|
||||
scopeguard = "1.2.0"
|
||||
serde = { version = "1.0.229", features = [ "derive" ] }
|
||||
serde_json = "1.0.151"
|
||||
serde_with = "3.21.0"
|
||||
strum = { version = "0.28.0", features = [ "derive" ] }
|
||||
syntect = { version = "5.3.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }
|
||||
thiserror = "2.0.19"
|
||||
tokio = { version = "1.53.0", features = [ "full" ] }
|
||||
tokio-stream = "0.1.18"
|
||||
tokio-util = "0.7.18"
|
||||
toml = { version = "1.1.3" }
|
||||
tracing = { version = "0.1.44", features = [ "max_level_debug", "release_max_level_debug" ] }
|
||||
twox-hash = { version = "2.1.3", default-features = false, features = [ "std", "random", "xxhash3_128" ] }
|
||||
typed-path = "0.12.3"
|
||||
unicode-width = { version = "0.2.2", default-features = false }
|
||||
uzers = "0.12.2"
|
||||
|
||||
[workspace.lints.clippy]
|
||||
format_push_string = "warn"
|
||||
if_same_then_else = "allow"
|
||||
implicit_clone = "warn"
|
||||
len_without_is_empty = "allow"
|
||||
missing_safety_doc = "allow"
|
||||
module_inception = "allow"
|
||||
option_map_unit_fn = "allow"
|
||||
unit_arg = "allow"
|
||||
use_self = "warn"
|
||||
serde = { version = "1.0.219", features = [ "derive" ] }
|
||||
serde_json = "1.0.140"
|
||||
syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }
|
||||
tokio = { version = "1.45.1", features = [ "full" ] }
|
||||
tokio-stream = "0.1.17"
|
||||
tokio-util = "0.7.15"
|
||||
toml = { version = "0.8.22" }
|
||||
tracing = { version = "0.1.41", features = [ "max_level_debug", "release_max_level_debug" ] }
|
||||
twox-hash = { version = "2.1.0", default-features = false, features = [ "std", "random", "xxhash3_128" ] }
|
||||
unicode-width = { version = "0.2.0", default-features = false }
|
||||
uzers = "0.12.1"
|
||||
|
|
|
|||
66
README.md
66
README.md
|
|
@ -1,11 +1,17 @@
|
|||
<div align="center">
|
||||
<sup>Special thanks to:</sup><br>
|
||||
|
||||
| <a href="https://go.warp.dev/yazi" target="_blank"><img alt="Warp sponsorship" width=350 src="https://github.com/warpdotdev/brand-assets/blob/main/Github/Sponsor/Warp-Github-LG-02.png"><br><b>Warp, built for coding with multiple AI agents</b><br><sup>Available for macOS, Linux and Windows</sup></a> |
|
||||
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
<sup>Special thanks to:</sup>
|
||||
<br><br>
|
||||
<a href="https://www.warp.dev/?utm_source=github&utm_medium=referral&utm_campaign=yazi" target="_blank">
|
||||
<img alt="Warp sponsorship" width="300" src="https://github.com/user-attachments/assets/c7f141e7-9751-407d-bb0e-d6f2c487b34f">
|
||||
<br>
|
||||
<b>Warp, the intelligent terminal</b>
|
||||
<br>
|
||||
<sup>Yazi's AI-powered terminal of choice!<br>Available for macOS, Linux and Windows</sup>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
## Yazi - ⚡️ Blazing Fast Terminal File Manager
|
||||
|
||||
Yazi (means "duck") is a terminal file manager written in Rust, based on non-blocking async I/O. It aims to provide an efficient, user-friendly, and customizable file management experience.
|
||||
|
|
@ -17,14 +23,13 @@ Yazi (means "duck") is a terminal file manager written in Rust, based on non-blo
|
|||
- 🖼️ **Built-in Support for Multiple Image Protocols**: Also integrated with Überzug++ and Chafa, covering almost all terminals.
|
||||
- 🌟 **Built-in Code Highlighting and Image Decoding**: Combined with the pre-loading mechanism, greatly accelerates image and normal file loading.
|
||||
- 🔌 **Concurrent Plugin System**: UI plugins (rewriting most of the UI), functional plugins, custom previewer/preloader/spotter/fetcher; Just some pieces of Lua.
|
||||
- ☁️ **Virtual Filesystem**: Remote file management, custom search engines.
|
||||
- 📡 **Data Distribution Service**: Built on a client-server architecture (no additional server process required), integrated with a Lua-based publish-subscribe model, achieving cross-instance communication and state persistence.
|
||||
- 📦 **Package Manager**: Install plugins and themes with one command, keeping them up-to-date, or pin them to a specific version.
|
||||
- 🧰 Integration with ripgrep, fd, fzf, zoxide
|
||||
- 💫 Vim-like input/pick/confirm/which/notify component, auto-completion for cd paths
|
||||
- 🏷️ Multi-Tab Support, Cross-directory selection, Scrollable Preview (for videos, PDFs, archives, code, directories, etc.)
|
||||
- 🔄 Bulk Rename/Create, Archive Extraction, Visual Mode, File Chooser, [Git Integration](https://github.com/yazi-rs/plugins/tree/main/git.yazi), [Mount Manager](https://github.com/yazi-rs/plugins/tree/main/mount.yazi)
|
||||
- 🎨 Theme System, Mouse Support, Drag and Drop, Trash Bin, Custom Layouts, CSI u, OSC 52
|
||||
- 🔄 Bulk Renaming, Archive Extraction, Visual Mode, File Chooser, [Git Integration](https://github.com/yazi-rs/plugins/tree/main/git.yazi), [Mount Manager](https://github.com/yazi-rs/plugins/tree/main/mount.yazi)
|
||||
- 🎨 Theme System, Mouse Support, Trash Bin, Custom Layouts, CSI u, OSC 52
|
||||
- ... and more!
|
||||
|
||||
https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265cc7
|
||||
|
|
@ -47,24 +52,25 @@ Yazi is currently in heavy development, expect breaking changes.
|
|||
|
||||
## Image Preview
|
||||
|
||||
| Platform | Protocol | Support |
|
||||
| ---------------------------------------------------------------------------- | -------------------------------------- | -------------------------------------- |
|
||||
| [kitty](https://github.com/kovidgoyal/kitty) (>= 0.28.0) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
|
||||
| [iTerm2](https://iterm2.com) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [WezTerm](https://github.com/wez/wezterm) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [Konsole](https://invent.kde.org/utilities/konsole) | [Kitty old protocol][kgp-old] | ✅ Built-in |
|
||||
| [foot](https://codeberg.org/dnkl/foot) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||
| [Ghostty](https://github.com/ghostty-org/ghostty) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
|
||||
| [Windows Terminal](https://github.com/microsoft/terminal) (>= v1.22.10352.0) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||
| [st with Sixel patch](https://github.com/bakkeby/st-flexipatch) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||
| [Warp](https://www.warp.dev) (macOS/Linux only) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [Tabby](https://github.com/Eugeny/tabby) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [VSCode](https://github.com/microsoft/vscode) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [Rio](https://github.com/raphamorim/rio) (>= 0.3.9) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
|
||||
| [Black Box](https://gitlab.gnome.org/raggesilver/blackbox) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||
| [Bobcat](https://github.com/ismail-yilmaz/Bobcat) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| X11 / Wayland | Window system protocol | ☑️ [Überzug++][ueberzug] required |
|
||||
| Fallback | [ASCII art (Unicode block)][ascii-art] | ☑️ [Chafa][chafa] required (>= 1.16.0) |
|
||||
| Platform | Protocol | Support |
|
||||
| ---------------------------------------------------------------------------- | -------------------------------------- | ----------------------------------------------------- |
|
||||
| [kitty](https://github.com/kovidgoyal/kitty) (>= 0.28.0) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
|
||||
| [iTerm2](https://iterm2.com) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [WezTerm](https://github.com/wez/wezterm) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [Konsole](https://invent.kde.org/utilities/konsole) | [Kitty old protocol][kgp-old] | ✅ Built-in |
|
||||
| [foot](https://codeberg.org/dnkl/foot) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||
| [Ghostty](https://github.com/ghostty-org/ghostty) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
|
||||
| [Windows Terminal](https://github.com/microsoft/terminal) (>= v1.22.10352.0) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||
| [st with Sixel patch](https://github.com/bakkeby/st-flexipatch) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||
| [Warp](https://www.warp.dev) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [Tabby](https://github.com/Eugeny/tabby) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [VSCode](https://github.com/microsoft/vscode) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [Rio](https://github.com/raphamorim/rio) | [Inline images protocol][iip] | ❌ Rio doesn't correctly clear images [#709][rio-bug] |
|
||||
| [Black Box](https://gitlab.gnome.org/raggesilver/blackbox) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||
| [Hyper](https://github.com/vercel/hyper) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [Bobcat](https://github.com/ismail-yilmaz/Bobcat) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| X11 / Wayland | Window system protocol | ☑️ [Überzug++][ueberzug] required |
|
||||
| Fallback | [ASCII art (Unicode block)][ascii-art] | ☑️ [Chafa][chafa] required |
|
||||
|
||||
See https://yazi-rs.github.io/docs/image-preview for details.
|
||||
|
||||
|
|
@ -81,13 +87,9 @@ See https://yazi-rs.github.io/docs/image-preview for details.
|
|||
[ueberzug]: https://github.com/jstkdng/ueberzugpp
|
||||
[chafa]: https://hpjansson.org/chafa/
|
||||
|
||||
## Special Thanks
|
||||
<!-- Rio bug -->
|
||||
|
||||
<img alt="RustRover logo" align="right" width="200" src="https://resources.jetbrains.com/storage/products/company/brand/logos/RustRover.svg">
|
||||
|
||||
Thanks to RustRover team for providing open-source licenses to support the maintenance of Yazi.
|
||||
|
||||
Active code contributors can contact @sxyazi to get a license (if any are still available).
|
||||
[rio-bug]: https://github.com/raphamorim/rio/issues/709
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
[Desktop Entry]
|
||||
Name=Yazi File Manager
|
||||
Name=Yazi
|
||||
Icon=yazi
|
||||
Comment=Blazing fast terminal file manager written in Rust, based on async I/O
|
||||
Terminal=true
|
||||
TryExec=yazi
|
||||
Exec=yazi %f
|
||||
Exec=yazi %u
|
||||
Type=Application
|
||||
MimeType=inode/directory
|
||||
Categories=System;FileManager;FileTools;ConsoleOnly
|
||||
Categories=Utility;Core;System;FileTools;FileManager;ConsoleOnly
|
||||
Keywords=File;Manager;Explorer;Browser;Launcher
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"flagWords":[],"version":"0.2","language":"en","words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing","treelize","TOCTOU","fellback","watchee","Textlike","sstr","pointee","writef","wakeup","nonblocking","sigwinch","timespec","termios","tcgetattr","tcsetattr","tcgetwinsize","rustix","codepoint","codepoints","Raterm","mimetypes","Mimelist","renderables","redrawer","Padable"]}
|
||||
{"version":"0.2","language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg"]}
|
||||
12
flake.lock
generated
12
flake.lock
generated
|
|
@ -20,11 +20,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1783279667,
|
||||
"narHash": "sha256-/NAkDSsve+GNM0Bt6tleJdCGfsTlK89nPjkVOzZMo0s=",
|
||||
"lastModified": 1747312588,
|
||||
"narHash": "sha256-MmJvj6mlWzeRwKGLcwmZpKaOPZ5nJb/6al5CXqJsgjo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "f205b5574fd0cb7da5b702a2da51507b7f4fdd1b",
|
||||
"rev": "b1bebd0fe266bbd1820019612ead889e96a8fa2d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -48,11 +48,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1783320166,
|
||||
"narHash": "sha256-l7C/OsjcnWDOk2K3ssj+SBduwL67LashjBqis9+t468=",
|
||||
"lastModified": 1747363019,
|
||||
"narHash": "sha256-N4dwkRBmpOosa4gfFkFf/LTD8oOcNkAyvZ07JvRDEf0=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "20ee15370c9256669d66968b89ee20a4b0a4e673",
|
||||
"rev": "0e624f2b1972a34be1a9b35290ed18ea4b419b6f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
17
flake.nix
17
flake.nix
|
|
@ -32,7 +32,7 @@
|
|||
rev = self.shortRev or self.dirtyShortRev or "dirty";
|
||||
date = self.lastModifiedDate or self.lastModified or "19700101";
|
||||
version =
|
||||
(builtins.fromTOML (builtins.readFile ./Cargo.toml)).workspace.package.version
|
||||
(builtins.fromTOML (builtins.readFile ./yazi-fm/Cargo.toml)).package.version
|
||||
+ "pre${builtins.substring 0 8 date}_${rev}";
|
||||
in
|
||||
{
|
||||
|
|
@ -45,28 +45,21 @@
|
|||
rustPlatform
|
||||
;
|
||||
};
|
||||
yazi = pkgs.callPackage ./nix/yazi.nix {
|
||||
inherit (self.packages.${system}) yazi-unwrapped;
|
||||
};
|
||||
yazi = pkgs.callPackage ./nix/yazi.nix { inherit (self.packages.${system}) yazi-unwrapped; };
|
||||
default = self.packages.${system}.yazi;
|
||||
};
|
||||
|
||||
devShells = {
|
||||
default = pkgs.callPackage ./nix/shell.nix {
|
||||
inherit toolchain;
|
||||
inherit (self.packages.${system}) yazi yazi-unwrapped;
|
||||
};
|
||||
default = pkgs.callPackage ./nix/shell.nix { };
|
||||
};
|
||||
|
||||
formatter = pkgs.nixfmt-tree;
|
||||
formatter = pkgs.nixfmt-rfc-style;
|
||||
}
|
||||
)
|
||||
// {
|
||||
overlays = {
|
||||
default = self.overlays.yazi;
|
||||
yazi = _: prev: {
|
||||
inherit (self.packages.${prev.stdenv.system}) yazi yazi-unwrapped;
|
||||
};
|
||||
yazi = _: prev: { inherit (self.packages.${prev.stdenv.system}) yazi yazi-unwrapped; };
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
{
|
||||
mkShell,
|
||||
yazi,
|
||||
toolchain,
|
||||
cspell,
|
||||
yazi-unwrapped,
|
||||
callPackage,
|
||||
rust-bin,
|
||||
nodePackages,
|
||||
}:
|
||||
|
||||
mkShell {
|
||||
packages = yazi.passthru.runtimePaths ++ [
|
||||
(toolchain.override {
|
||||
let
|
||||
mainPkg = callPackage ./yazi.nix { };
|
||||
in
|
||||
mainPkg.overrideAttrs (oa: {
|
||||
nativeBuildInputs = [
|
||||
(rust-bin.stable.latest.default.override {
|
||||
extensions = [
|
||||
"rust-src"
|
||||
"rustfmt"
|
||||
|
|
@ -16,10 +16,9 @@ mkShell {
|
|||
"clippy"
|
||||
];
|
||||
})
|
||||
cspell
|
||||
];
|
||||
|
||||
inputsFrom = [ yazi-unwrapped ];
|
||||
nodePackages.cspell
|
||||
] ++ (oa.nativeBuildInputs or [ ]);
|
||||
|
||||
env.RUST_BACKTRACE = "1";
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
lib,
|
||||
|
||||
installShellFiles,
|
||||
fetchFromGitHub,
|
||||
stdenv,
|
||||
darwin,
|
||||
rust-jemalloc-sys,
|
||||
|
||||
imagemagick,
|
||||
|
|
@ -22,12 +23,15 @@ let
|
|||
];
|
||||
};
|
||||
in
|
||||
rustPlatform.buildRustPackage (finalAttrs: {
|
||||
rustPlatform.buildRustPackage rec {
|
||||
pname = "yazi";
|
||||
inherit version src;
|
||||
|
||||
cargoLock = {
|
||||
lockFile = "${src}/Cargo.lock";
|
||||
#outputHashes = {
|
||||
# "mlua-0.10.0" = "sha256-Xg6/jc+UP8tbJJ6x1sbAgt8ZHt051xEBBcjmikQqYlw=";
|
||||
#};
|
||||
};
|
||||
|
||||
env = {
|
||||
|
|
@ -43,7 +47,7 @@ rustPlatform.buildRustPackage (finalAttrs: {
|
|||
|
||||
buildInputs = [
|
||||
rust-jemalloc-sys
|
||||
];
|
||||
] ++ lib.optionals stdenv.isDarwin (with darwin.apple_sdk.frameworks; [ Foundation ]);
|
||||
|
||||
postInstall = ''
|
||||
installShellCompletion --cmd yazi \
|
||||
|
|
@ -51,38 +55,20 @@ rustPlatform.buildRustPackage (finalAttrs: {
|
|||
--fish ./yazi-boot/completions/yazi.fish \
|
||||
--zsh ./yazi-boot/completions/_yazi
|
||||
|
||||
installShellCompletion --cmd ya \
|
||||
--bash ./yazi-cli/completions/ya.bash \
|
||||
--fish ./yazi-cli/completions/ya.fish \
|
||||
--zsh ./yazi-cli/completions/_ya
|
||||
|
||||
# Resize logo
|
||||
for RES in 16 24 32 48 64 128 256; do
|
||||
mkdir -p $out/share/icons/hicolor/"$RES"x"$RES"/apps
|
||||
magick assets/logo.png -resize "$RES"x"$RES" $out/share/icons/hicolor/"$RES"x"$RES"/apps/yazi.png
|
||||
done
|
||||
|
||||
installManPage ${finalAttrs.passthru.srcs.man_src}/yazi{.1,-config.5}
|
||||
|
||||
mkdir -p $out/share/applications
|
||||
install -m644 assets/yazi.desktop $out/share/applications/
|
||||
'';
|
||||
|
||||
passthru.srcs = {
|
||||
man_src = fetchFromGitHub {
|
||||
name = "manpages"; # needed to ensure name is unique
|
||||
owner = "yazi-rs";
|
||||
repo = "manpages";
|
||||
rev = "8950e968f4a1ad0b83d5836ec54a070855068dbf";
|
||||
hash = "sha256-kEVXejDg4ChFoMNBvKlwdFEyUuTcY2VuK9j0PdafKus=";
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
meta = {
|
||||
description = "Blazing fast terminal file manager written in Rust, based on async I/O";
|
||||
homepage = "https://github.com/sxyazi/yazi";
|
||||
license = lib.licenses.mit;
|
||||
mainProgram = "yazi";
|
||||
};
|
||||
})
|
||||
}
|
||||
|
|
|
|||
12
nix/yazi.nix
12
nix/yazi.nix
|
|
@ -12,12 +12,11 @@
|
|||
|
||||
# default optional deps
|
||||
jq,
|
||||
poppler-utils,
|
||||
poppler_utils,
|
||||
_7zz,
|
||||
ffmpeg,
|
||||
ffmpegthumbnailer,
|
||||
fd,
|
||||
ripgrep,
|
||||
resvg,
|
||||
fzf,
|
||||
zoxide,
|
||||
imagemagick,
|
||||
|
|
@ -39,12 +38,11 @@ let
|
|||
|
||||
defaultDeps = [
|
||||
jq
|
||||
poppler-utils
|
||||
poppler_utils
|
||||
_7zz
|
||||
ffmpeg
|
||||
ffmpegthumbnailer
|
||||
fd
|
||||
ripgrep
|
||||
resvg
|
||||
fzf
|
||||
zoxide
|
||||
imagemagick
|
||||
|
|
@ -96,8 +94,6 @@ runCommand yazi-unwrapped.name
|
|||
inherit (yazi-unwrapped) pname version meta;
|
||||
|
||||
nativeBuildInputs = [ makeWrapper ];
|
||||
|
||||
passthru.runtimePaths = runtimePaths;
|
||||
}
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
|
|
|
|||
|
|
@ -4,31 +4,21 @@ set -euo pipefail
|
|||
export ARTIFACT_NAME="yazi-$1"
|
||||
export YAZI_GEN_COMPLETIONS=1
|
||||
|
||||
# Build the target
|
||||
# Build for the target
|
||||
git config --global --add safe.directory "*"
|
||||
cargo build --release --locked --target "$1"
|
||||
|
||||
# Copy the binaries to a known location
|
||||
mkdir -p "target/release"
|
||||
cp "target/$1/release/ya" "target/release/ya"
|
||||
cp "target/$1/release/yazi" "target/release/yazi"
|
||||
|
||||
# Package deb
|
||||
if [[ "$ARTIFACT_NAME" == *-linux-* ]] && { [[ "$ARTIFACT_NAME" == *-aarch64-* ]] || [[ "$ARTIFACT_NAME" == *-x86_64-* ]]; }; then
|
||||
cargo install cargo-deb
|
||||
cargo deb -p yazi-packing --no-build --target "$1" -o "$ARTIFACT_NAME.deb"
|
||||
fi
|
||||
|
||||
# Create the artifact
|
||||
mkdir -p "$ARTIFACT_NAME/completions"
|
||||
cp "target/release/ya" "$ARTIFACT_NAME"
|
||||
cp "target/release/yazi" "$ARTIFACT_NAME"
|
||||
cp "target/$1/release/ya" "$ARTIFACT_NAME"
|
||||
cp "target/$1/release/yazi" "$ARTIFACT_NAME"
|
||||
cp yazi-cli/completions/* "$ARTIFACT_NAME/completions"
|
||||
cp yazi-boot/completions/* "$ARTIFACT_NAME/completions"
|
||||
cp README.md LICENSE "$ARTIFACT_NAME"
|
||||
|
||||
# Zip the artifact
|
||||
if ! command -v zip &> /dev/null; then
|
||||
apt-get update && apt-get install -yq zip
|
||||
if ! command -v zip &> /dev/null
|
||||
then
|
||||
sudo apt-get update && sudo apt-get install -yq zip
|
||||
fi
|
||||
zip -r "$ARTIFACT_NAME.zip" "$ARTIFACT_NAME"
|
||||
|
|
|
|||
|
|
@ -7,14 +7,7 @@ cd "$SCRIPT_DIR/.."
|
|||
echo "Bumping version: $1"
|
||||
|
||||
TOML_FILES="$(git ls-files '*Cargo.toml')"
|
||||
perl -pi -e 's/^version .*= .*$/version = "'"$1"'"/' -- $TOML_FILES
|
||||
perl -pi -e 's/^(yazi-[a-z]+)\s*=\s*{.*$/\1 = { path = "..\/\1", version = "'"$1"'" }/' -- $TOML_FILES
|
||||
perl -pi -e "s/^version .*= .*\$/version = \"$1\"/" -- $TOML_FILES
|
||||
perl -pi -e "s/^(yazi-[a-z]+)\\s*=\\s*{.*\$/\\1 = { path = \"..\/\\1\", version = \"$1\" }/" -- $TOML_FILES
|
||||
|
||||
# Insert "## [v$1]" after "## [Unreleased]"
|
||||
perl -0777 -pe "s/^(## \[Unreleased\]\s*)/\\1## [v$1]\n\n/m" -i CHANGELOG.md
|
||||
# Determine previous version and append compare link
|
||||
prev_ver=$(grep -oE '^\[v[0-9][^]]+\]' CHANGELOG.md | tail -n1 | tr -d '[]')
|
||||
link="\[v$1\]: https://github.com/sxyazi/yazi/compare/$prev_ver...v$1"
|
||||
perl -pi -e 's{(\['"$prev_ver"'\]:[^\n]+\n)}{$1'"$link"'\n}s' CHANGELOG.md
|
||||
|
||||
eslint -c ~/.config/rules/eslint/eslint.config.js --fix -- $TOML_FILES
|
||||
ESLINT_USE_FLAT_CONFIG=true eslint -c ~/.config/rules/eslint/eslint.config.cjs --fix -- $TOML_FILES
|
||||
|
|
|
|||
19
scripts/publish.sh
Executable file
19
scripts/publish.sh
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
cargo publish -p yazi-macro && sleep 30
|
||||
cargo publish -p yazi-codegen && sleep 30
|
||||
cargo publish -p yazi-shared && sleep 30
|
||||
cargo publish -p yazi-ffi && sleep 30
|
||||
cargo publish -p yazi-fs && sleep 30
|
||||
cargo publish -p yazi-config && sleep 30
|
||||
cargo publish -p yazi-proxy && sleep 30
|
||||
cargo publish -p yazi-adapter && sleep 30
|
||||
cargo publish -p yazi-boot && sleep 30
|
||||
cargo publish -p yazi-binding && sleep 30
|
||||
cargo publish -p yazi-dds && sleep 30
|
||||
cargo publish -p yazi-scheduler && sleep 30
|
||||
cargo publish -p yazi-plugin && sleep 30
|
||||
cargo publish -p yazi-widgets && sleep 30
|
||||
cargo publish -p yazi-core && sleep 30
|
||||
cargo publish -p yazi-fm && sleep 30
|
||||
cargo publish -p yazi-cli
|
||||
|
|
@ -2,32 +2,19 @@ const LABEL_NAME = "needs info"
|
|||
const RE_VERSION = /Yazi\s+Version\s*:\s\d+\.\d+\.\d+\s\(/gm
|
||||
const RE_DEPENDENCIES = /Dependencies\s+[/a-z]+\s*:\s/gm
|
||||
const RE_CHECKLIST = /#{3}\s+Checklist\s+(?:^-\s+\[x]\s+.+?(?:\n|\r\n|$)){2}/gm
|
||||
const RE_PR_CHECKLIST = /#{2}\s+Checklist\s+(?:^-\s+\[x]\s+.+?(?:\n|\r\n|$)){2}/gm
|
||||
|
||||
function pullRequestBody(content) {
|
||||
if (RE_PR_CHECKLIST.test(content)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return "All required checklist items must be checked in the PR description."
|
||||
}
|
||||
|
||||
function bugReportBody(creator, content, hash) {
|
||||
if (RE_DEPENDENCIES.test(content) && RE_CHECKLIST.test(content) && new RegExp(` \\(${hash}[a-f0-9]? `).test(content)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return `Hey @${creator}, thank you for opening the issue to help improve Yazi, appreciate it!
|
||||
|
||||
I noticed that you did not correctly follow the issue template. Please ensure that:
|
||||
return `Hey @${creator}, I noticed that you did not correctly follow the issue template. Please ensure that:
|
||||
|
||||
- The bug can still be reproduced on the [newest nightly build](https://yazi-rs.github.io/docs/installation/#binaries).
|
||||
- The environment info (\`ya env\`) is updated for the newest nightly.
|
||||
- The debug information (\`yazi --debug\`) is updated for the newest nightly.
|
||||
- The non-optional items in the checklist are checked.
|
||||
|
||||
Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity.
|
||||
|
||||
Our maintainers work on Yazi in their free time, this helps them work efficiently, understand your setup quickly, and find a more appropriate solution. Thanks for your understanding! 🙏
|
||||
`
|
||||
}
|
||||
|
||||
|
|
@ -36,26 +23,16 @@ function featureRequestBody(creator, content) {
|
|||
return null
|
||||
}
|
||||
|
||||
return `Hey @${creator}, thank you for opening the issue to help improve Yazi, appreciate it!
|
||||
|
||||
I noticed that you did not correctly follow the issue template. Please ensure that:
|
||||
return `Hey @${creator}, I noticed that you did not correctly follow the issue template. Please ensure that:
|
||||
|
||||
- The requested feature does not exist in the [newest nightly build](https://yazi-rs.github.io/docs/installation/#binaries).
|
||||
- The environment info (\`ya env\`) is updated for the newest nightly.
|
||||
- The debug information (\`yazi --debug\`) is updated for the newest nightly.
|
||||
- The non-optional items in the checklist are checked.
|
||||
|
||||
Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity.
|
||||
|
||||
Our maintainers work on Yazi in their free time, this helps them work efficiently, understand your setup quickly, and find a more appropriate solution. Thanks for your understanding! 🙏
|
||||
`
|
||||
}
|
||||
|
||||
function skipValidation(context) {
|
||||
const login = context.payload.issue?.user?.login || context.payload.pull_request?.user?.login
|
||||
const owner = context.payload.repository?.owner?.login || context.repo.owner
|
||||
return !!login && login === owner
|
||||
}
|
||||
|
||||
module.exports = async ({ github, context, core }) => {
|
||||
async function nightlyHash() {
|
||||
try {
|
||||
|
|
@ -89,7 +66,7 @@ module.exports = async ({ github, context, core }) => {
|
|||
const { data: events } = await github.rest.issues.listEvents({
|
||||
...context.repo,
|
||||
issue_number: id,
|
||||
per_page: 100,
|
||||
per_page : 100,
|
||||
})
|
||||
|
||||
const all = events.filter(v => v.event === "labeled" && v.label?.name === LABEL_NAME)
|
||||
|
|
@ -105,7 +82,7 @@ module.exports = async ({ github, context, core }) => {
|
|||
const { data: events } = await github.rest.issues.listEvents({
|
||||
...context.repo,
|
||||
issue_number: id,
|
||||
per_page: 100,
|
||||
per_page : 100,
|
||||
})
|
||||
|
||||
const all = events.filter(v => v.event === "unlabeled" && v.label?.name === LABEL_NAME)
|
||||
|
|
@ -124,16 +101,14 @@ module.exports = async ({ github, context, core }) => {
|
|||
await github.rest.issues.removeLabel({
|
||||
...context.repo,
|
||||
issue_number: id,
|
||||
name: LABEL_NAME,
|
||||
name : LABEL_NAME,
|
||||
})
|
||||
await hideOldComments(id)
|
||||
} else if (mark && !marked && !await removedLabelManually(id)) {
|
||||
await github.rest.issues.addLabels({
|
||||
...context.repo,
|
||||
issue_number: id,
|
||||
labels: [LABEL_NAME],
|
||||
labels : [LABEL_NAME],
|
||||
})
|
||||
await hideOldComments(id)
|
||||
await github.rest.issues.createComment({
|
||||
...context.repo,
|
||||
issue_number: id,
|
||||
|
|
@ -145,42 +120,11 @@ module.exports = async ({ github, context, core }) => {
|
|||
}
|
||||
}
|
||||
|
||||
async function hideOldComments(id) {
|
||||
try {
|
||||
const comments = await github.paginate(github.rest.issues.listComments, {
|
||||
...context.repo,
|
||||
issue_number: id,
|
||||
per_page: 100,
|
||||
})
|
||||
|
||||
for (const c of comments) {
|
||||
const byBot = c.user?.login?.endsWith("[bot]") || c.user?.type === "Bot"
|
||||
const contains = c?.body?.includes("or closed after 2 days of inactivity")
|
||||
if (!byBot || !contains || !c.node_id) continue
|
||||
|
||||
try {
|
||||
await github.graphql(
|
||||
`mutation($subjectId: ID!, $classifier: ReportedContentClassifiers!) {
|
||||
minimizeComment(input: {subjectId: $subjectId, classifier: $classifier}) {
|
||||
minimizedComment { isMinimized }
|
||||
}
|
||||
}`,
|
||||
{ subjectId: c.node_id, classifier: "OUTDATED" },
|
||||
)
|
||||
} catch (e) {
|
||||
core.error(`Error minimizing comment ${c.id}: ${e.message}`)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
core.error(`Error listing comments: ${e.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function closeOldIssues() {
|
||||
try {
|
||||
const { data: issues } = await github.rest.issues.listForRepo({
|
||||
...context.repo,
|
||||
state: "open",
|
||||
state : "open",
|
||||
labels: LABEL_NAME,
|
||||
})
|
||||
|
||||
|
|
@ -193,13 +137,13 @@ module.exports = async ({ github, context, core }) => {
|
|||
await github.rest.issues.update({
|
||||
...context.repo,
|
||||
issue_number: issue.number,
|
||||
state: "closed",
|
||||
state : "closed",
|
||||
state_reason: "not_planned",
|
||||
})
|
||||
await github.rest.issues.createComment({
|
||||
...context.repo,
|
||||
issue_number: issue.number,
|
||||
body: `This issue has been automatically closed because it was marked as \`${LABEL_NAME}\` for more than 2 days without updates.
|
||||
body : `This issue has been automatically closed because it was marked as \`${LABEL_NAME}\` for more than 2 days without updates.
|
||||
If the problem persists, please file a new issue and complete the issue template so we can capture all the details necessary to investigate further.`,
|
||||
})
|
||||
}
|
||||
|
|
@ -209,58 +153,26 @@ If the problem persists, please file a new issue and complete the issue template
|
|||
}
|
||||
}
|
||||
|
||||
async function closeUnsupportedIssue(id) {
|
||||
try {
|
||||
await github.rest.issues.update({
|
||||
...context.repo,
|
||||
issue_number: id,
|
||||
state: "closed",
|
||||
state_reason: "not_planned",
|
||||
})
|
||||
await github.rest.issues.createComment({
|
||||
...context.repo,
|
||||
issue_number: id,
|
||||
body: `Unsupported issue template.
|
||||
Either the [Bug Report](https://github.com/sxyazi/yazi/issues/new?template=bug.yml) or [Feature Request](https://github.com/sxyazi/yazi/issues/new?template=feature.yml) template should be used.`,
|
||||
})
|
||||
} catch (e) {
|
||||
core.error(`Error closing unsupported issue: ${e.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const hash = await nightlyHash()
|
||||
if (!hash) return
|
||||
|
||||
if (context.eventName === "schedule") {
|
||||
await closeOldIssues()
|
||||
return
|
||||
}
|
||||
|
||||
if (skipValidation(context)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (context.eventName === "issues") {
|
||||
const id = context.payload.issue.number
|
||||
const content = context.payload.issue.body || ""
|
||||
const creator = context.payload.issue.user.login
|
||||
|
||||
const hash = await nightlyHash()
|
||||
if (!hash) return
|
||||
|
||||
if (await hasLabel(id, "bug")) {
|
||||
const body = bugReportBody(creator, content, hash)
|
||||
await updateLabels(id, !!body, body)
|
||||
} else if (await hasLabel(id, "feature")) {
|
||||
const body = featureRequestBody(creator, content)
|
||||
await updateLabels(id, !!body, body)
|
||||
} else if (context.payload.action === "opened") {
|
||||
await closeUnsupportedIssue(id)
|
||||
}
|
||||
} else if (context.eventName === "pull_request" || context.eventName === "pull_request_target") {
|
||||
const content = context.payload.pull_request.body || ""
|
||||
|
||||
const body = pullRequestBody(content)
|
||||
if (body) {
|
||||
core.setFailed(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
293
scripts/validate-form/package-lock.json
generated
293
scripts/validate-form/package-lock.json
generated
|
|
@ -8,225 +8,247 @@
|
|||
"name": "validate-form",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@actions/core": "^3.0.1",
|
||||
"@actions/github": "^9.1.1"
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/github": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/core": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-3.0.1.tgz",
|
||||
"integrity": "sha512-a6d/Nwahm9fliVGRhdhofo40HjHQasUPusmc7vBfyky+7Z+P2A1J68zyFVaNcEclc/Se+eO595oAr5nwEIoIUA==",
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
|
||||
"integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/exec": "^3.0.0",
|
||||
"@actions/http-client": "^4.0.0"
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/http-client": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/exec": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-3.0.0.tgz",
|
||||
"integrity": "sha512-6xH/puSoNBXb72VPlZVm7vQ+svQpFyA96qdDBvhB8eNZOE8LtPf9L4oAsfzK/crCL8YZ+19fKYVnM63Sl+Xzlw==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
|
||||
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/io": "^3.0.2"
|
||||
"@actions/io": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/github": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-9.1.1.tgz",
|
||||
"integrity": "sha512-tL5JbYOBZHc0ngEnCsaDcryUizIUIlQyIMwy1Wkx93H5HzbBJ7TbiPx2PnFjBwZW0Vh05JmfFZhecE6gglYegA==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-6.0.1.tgz",
|
||||
"integrity": "sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^3.0.2",
|
||||
"@octokit/core": "^7.0.6",
|
||||
"@octokit/plugin-paginate-rest": "^14.0.0",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
|
||||
"@octokit/request": "^10.0.7",
|
||||
"@octokit/request-error": "^7.1.0",
|
||||
"undici": "^6.23.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/github/node_modules/@actions/http-client": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.2.tgz",
|
||||
"integrity": "sha512-JP38FYYpyqvUsz+Igqlc/JG6YO9PaKuvqjM3iGvaLqFnJ7TFmcLyy2IDrY0bI0qCQug8E9K+elv5ZNfw62ZJzA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tunnel": "^0.0.6",
|
||||
"undici": "^6.23.0"
|
||||
"@actions/http-client": "^2.2.0",
|
||||
"@octokit/core": "^5.0.1",
|
||||
"@octokit/plugin-paginate-rest": "^9.2.2",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^10.4.0",
|
||||
"@octokit/request": "^8.4.1",
|
||||
"@octokit/request-error": "^5.1.1",
|
||||
"undici": "^5.28.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/http-client": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-4.0.1.tgz",
|
||||
"integrity": "sha512-+Nvd1ImaOZBSoPbsUtEhv+1z99H12xzncCkz0a3RuehINE81FZSe2QTj3uvAPTcJX/SCzUQHQ0D1GrPMbrPitg==",
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
|
||||
"integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tunnel": "^0.0.6",
|
||||
"undici": "^6.23.0"
|
||||
"undici": "^5.25.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/io": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@actions/io/-/io-3.0.2.tgz",
|
||||
"integrity": "sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
|
||||
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/auth-token": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz",
|
||||
"integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==",
|
||||
"node_modules/@fastify/busboy": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
|
||||
"integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-token": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
|
||||
"integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz",
|
||||
"integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==",
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.1.tgz",
|
||||
"integrity": "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^6.0.0",
|
||||
"@octokit/graphql": "^9.0.3",
|
||||
"@octokit/request": "^10.0.6",
|
||||
"@octokit/request-error": "^7.0.2",
|
||||
"@octokit/types": "^16.0.0",
|
||||
"before-after-hook": "^4.0.0",
|
||||
"universal-user-agent": "^7.0.0"
|
||||
"@octokit/auth-token": "^4.0.0",
|
||||
"@octokit/graphql": "^7.1.0",
|
||||
"@octokit/request": "^8.4.1",
|
||||
"@octokit/request-error": "^5.1.1",
|
||||
"@octokit/types": "^13.0.0",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/endpoint": {
|
||||
"version": "11.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.3.tgz",
|
||||
"integrity": "sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==",
|
||||
"version": "9.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
|
||||
"integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^16.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
"@octokit/types": "^13.1.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz",
|
||||
"integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz",
|
||||
"integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/request": "^10.0.6",
|
||||
"@octokit/types": "^16.0.0",
|
||||
"universal-user-agent": "^7.0.0"
|
||||
"@octokit/request": "^8.4.1",
|
||||
"@octokit/types": "^13.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/openapi-types": {
|
||||
"version": "27.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz",
|
||||
"integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==",
|
||||
"version": "24.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
|
||||
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz",
|
||||
"integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==",
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz",
|
||||
"integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^16.0.0"
|
||||
"@octokit/types": "^12.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=6"
|
||||
"@octokit/core": "5"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": {
|
||||
"version": "20.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
|
||||
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": {
|
||||
"version": "12.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
|
||||
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz",
|
||||
"integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==",
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz",
|
||||
"integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^16.0.0"
|
||||
"@octokit/types": "^12.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=6"
|
||||
"@octokit/core": "5"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": {
|
||||
"version": "20.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
|
||||
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": {
|
||||
"version": "12.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
|
||||
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request": {
|
||||
"version": "10.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.8.tgz",
|
||||
"integrity": "sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw==",
|
||||
"version": "8.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
|
||||
"integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^11.0.3",
|
||||
"@octokit/request-error": "^7.0.2",
|
||||
"@octokit/types": "^16.0.0",
|
||||
"fast-content-type-parse": "^3.0.0",
|
||||
"json-with-bigint": "^3.5.3",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
"@octokit/endpoint": "^9.0.6",
|
||||
"@octokit/request-error": "^5.1.1",
|
||||
"@octokit/types": "^13.1.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request-error": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz",
|
||||
"integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==",
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
|
||||
"integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^16.0.0"
|
||||
"@octokit/types": "^13.1.0",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/types": {
|
||||
"version": "16.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz",
|
||||
"integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==",
|
||||
"version": "13.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
|
||||
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^27.0.0"
|
||||
"@octokit/openapi-types": "^24.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/before-after-hook": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz",
|
||||
"integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==",
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
||||
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/fast-content-type-parse": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz",
|
||||
"integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
"node_modules/deprecation": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
|
||||
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/json-with-bigint": {
|
||||
"version": "3.5.8",
|
||||
"resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.8.tgz",
|
||||
"integrity": "sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==",
|
||||
"license": "MIT"
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/tunnel": {
|
||||
"version": "0.0.6",
|
||||
|
|
@ -238,18 +260,27 @@
|
|||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "6.27.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.27.0.tgz",
|
||||
"integrity": "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==",
|
||||
"version": "5.29.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
|
||||
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/busboy": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.17"
|
||||
"node": ">=14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/universal-user-agent": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
|
||||
"integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
|
||||
"integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"license": "ISC"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@actions/core": "^3.0.1",
|
||||
"@actions/github": "^9.1.1"
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/github": "^6.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,6 @@ apps:
|
|||
command: yazi
|
||||
environment:
|
||||
PATH: $SNAP/bin:$SNAP/usr/bin:$PATH
|
||||
ya:
|
||||
command: ya
|
||||
environment:
|
||||
PATH: $SNAP/bin:$SNAP/usr/bin:$PATH
|
||||
|
||||
parts:
|
||||
yazi:
|
||||
|
|
@ -34,7 +30,7 @@ parts:
|
|||
- chafa
|
||||
- fd-find
|
||||
- ffmpeg
|
||||
# - fzf
|
||||
- fzf
|
||||
- libglu1-mesa
|
||||
- libglut3.12
|
||||
- poppler-utils
|
||||
|
|
@ -123,26 +119,17 @@ parts:
|
|||
# expected binary without much overhead, and works across platforms.
|
||||
magick:
|
||||
plugin: autotools
|
||||
source: https://github.com/ImageMagick/ImageMagick.git
|
||||
source-type: git
|
||||
source-tag: 7.1.2-0
|
||||
source-depth: 1
|
||||
source: https://imagemagick.org/archive/ImageMagick.tar.gz
|
||||
source-type: tar
|
||||
stage-packages:
|
||||
- libgomp1
|
||||
autotools-configure-parameters:
|
||||
- --prefix=/usr
|
||||
build-attributes:
|
||||
- enable-patchelf
|
||||
organize:
|
||||
# `libgomp1` installs libgomp into the multiarch directory, but the
|
||||
# `magick` binary's RPATH only searches `$ORIGIN/../lib` (i.e. usr/lib).
|
||||
# Move it alongside the Magick libraries so the bundled binary can load
|
||||
# it at runtime; otherwise magick fails with:
|
||||
# "libgomp.so.1: cannot open shared object file: No such file or directory"
|
||||
usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libgomp.so*: usr/lib/
|
||||
prime:
|
||||
- usr/bin/magick
|
||||
- usr/lib/libgomp.so*
|
||||
- usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libgomp.so.1
|
||||
- usr/lib/libMagickCore-7.Q16HDRI.so*
|
||||
- usr/lib/libMagickWand-7.Q16HDRI.so*
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
syntax = "Lua54"
|
||||
indent_width = 2
|
||||
call_parentheses = "NoSingleTable"
|
||||
collapse_simple_statement = "FunctionOnly"
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
[package]
|
||||
name = "yazi-actor"
|
||||
description = "Yazi actor model"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
default = [ "vendored-lua" ]
|
||||
vendored-lua = [ "mlua/vendored" ]
|
||||
|
||||
[dependencies]
|
||||
yazi-binding = { path = "../yazi-binding", version = "26.5.6" }
|
||||
yazi-boot = { path = "../yazi-boot", version = "26.5.6" }
|
||||
yazi-config = { path = "../yazi-config", version = "26.5.6" }
|
||||
yazi-core = { path = "../yazi-core", version = "26.5.6" }
|
||||
yazi-dds = { path = "../yazi-dds", version = "26.5.6" }
|
||||
yazi-emulator = { path = "../yazi-emulator", version = "26.5.6" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "26.5.6" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "26.5.6" }
|
||||
yazi-parser = { path = "../yazi-parser", version = "26.5.6" }
|
||||
yazi-plugin = { path = "../yazi-plugin", version = "26.5.6" }
|
||||
yazi-proxy = { path = "../yazi-proxy", version = "26.5.6" }
|
||||
yazi-runner = { path = "../yazi-runner", version = "26.5.6" }
|
||||
yazi-scheduler = { path = "../yazi-scheduler", version = "26.5.6" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "26.5.6" }
|
||||
yazi-shim = { path = "../yazi-shim", version = "26.5.6" }
|
||||
yazi-term = { path = "../yazi-term", version = "26.5.6" }
|
||||
yazi-tty = { path = "../yazi-tty", version = "26.5.9" }
|
||||
yazi-tui = { path = "../yazi-tui", version = "26.5.6" }
|
||||
yazi-vfs = { path = "../yazi-vfs", version = "26.5.6" }
|
||||
yazi-watcher = { path = "../yazi-watcher", version = "26.5.6" }
|
||||
yazi-widgets = { path = "../yazi-widgets", version = "26.5.6" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
either = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
hashbrown = { workspace = true }
|
||||
indexmap = { workspace = true }
|
||||
inventory = { workspace = true }
|
||||
mlua = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
ratatui-core = { workspace = true }
|
||||
scopeguard = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tokio-stream = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
[target."cfg(unix)".dependencies]
|
||||
libc = { workspace = true }
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
# yazi-actor
|
||||
|
||||
This crate is part of [Yazi][source], and it is not supposed to be used outside, as there are no guarantees about the stability of its API.
|
||||
|
||||
[source]: https://github.com/sxyazi/yazi
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_parser::spark::SparkKind;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::Ctx;
|
||||
|
||||
pub trait Actor {
|
||||
type Form;
|
||||
|
||||
const NAME: &str;
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data>;
|
||||
|
||||
fn hook(_cx: &Ctx, _form: &Self::Form) -> Option<SparkKind> { None }
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use mlua::IntoLua;
|
||||
use tracing::error;
|
||||
use yazi_actor::lives::Lives;
|
||||
use yazi_binding::runtime_scope;
|
||||
use yazi_dds::{LOCAL, Payload, REMOTE};
|
||||
use yazi_macro::succ;
|
||||
use yazi_plugin::LUA;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct AcceptPayload;
|
||||
|
||||
impl Actor for AcceptPayload {
|
||||
type Form = Payload<'static>;
|
||||
|
||||
const NAME: &str = "accept_payload";
|
||||
|
||||
fn act(cx: &mut Ctx, payload: Payload) -> Result<Data> {
|
||||
let kind = payload.body.kind();
|
||||
let lock = if payload.receiver == 0 || payload.receiver != payload.sender {
|
||||
REMOTE.read()
|
||||
} else {
|
||||
LOCAL.read()
|
||||
};
|
||||
|
||||
let Some(handlers) = lock.get(kind).filter(|&m| !m.is_empty()).cloned() else { succ!() };
|
||||
drop(lock);
|
||||
|
||||
let kind = kind.to_owned();
|
||||
succ!(Lives::scope(cx.core, |_| {
|
||||
let body = payload.body.into_lua(&LUA)?;
|
||||
for (id, cb) in handlers {
|
||||
if let Err(e) = runtime_scope!(LUA, &id, cb.call::<()>(body.clone())) {
|
||||
error!("Failed to run `{kind}` event handler in your `{id}` plugin: {e}");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})?);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_actor::Ctx;
|
||||
use yazi_boot::BOOT;
|
||||
use yazi_core::mgr::CdSource;
|
||||
use yazi_macro::{act, succ};
|
||||
use yazi_parser::VoidForm;
|
||||
use yazi_shared::{data::Data, strand::StrandLike, url::UrlLike};
|
||||
|
||||
use crate::Actor;
|
||||
|
||||
pub struct Bootstrap;
|
||||
|
||||
impl Actor for Bootstrap {
|
||||
type Form = VoidForm;
|
||||
|
||||
const NAME: &str = "bootstrap";
|
||||
|
||||
fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
|
||||
cx.mgr.tabs.resize_with(BOOT.files.len(), Default::default);
|
||||
|
||||
for (i, file) in BOOT.files.iter().enumerate().rev() {
|
||||
cx.tab = i;
|
||||
if file.is_empty() {
|
||||
act!(mgr:cd, cx, (BOOT.cwds[i].clone(), CdSource::Tab))?;
|
||||
} else if let Ok(u) = BOOT.cwds[i].try_join(file) {
|
||||
act!(mgr:reveal, cx, (u, CdSource::Tab))?;
|
||||
}
|
||||
}
|
||||
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_core::notify::{MessageLevel, MessageOpt};
|
||||
use yazi_macro::act;
|
||||
use yazi_parser::app::DeprecateForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Deprecate;
|
||||
|
||||
impl Actor for Deprecate {
|
||||
type Form = DeprecateForm;
|
||||
|
||||
const NAME: &str = "deprecate";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
act!(notify:push, cx, MessageOpt {
|
||||
title: "Deprecated API".to_owned(),
|
||||
content: form.content.into_owned(),
|
||||
level: MessageLevel::Warn,
|
||||
timeout: std::time::Duration::from_secs(20),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use mlua::{ObjectLike, Table};
|
||||
use tracing::error;
|
||||
use yazi_actor::lives::Lives;
|
||||
use yazi_binding::runtime_scope;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::app::DndForm;
|
||||
use yazi_plugin::LUA;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Dnd;
|
||||
|
||||
impl Actor for Dnd {
|
||||
type Form = DndForm;
|
||||
|
||||
const NAME: &str = "dnd";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() };
|
||||
let area = yazi_binding::elements::Rect::from(size);
|
||||
|
||||
let result = Lives::scope(cx.core, move |_| {
|
||||
runtime_scope!(LUA, "root", {
|
||||
let root = LUA.globals().raw_get::<Table>("Root")?.call_method::<Table>("new", area)?;
|
||||
|
||||
if form.event.is_drag() {
|
||||
root.call_method::<()>("drag", form.event)?;
|
||||
} else {
|
||||
root.call_method::<()>("drop", form.event)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
if let Err(ref e) = result {
|
||||
error!("{e}");
|
||||
}
|
||||
succ!(result?);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_actor::Ctx;
|
||||
use yazi_macro::act;
|
||||
use yazi_parser::VoidForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::Actor;
|
||||
|
||||
pub struct Focus;
|
||||
|
||||
impl Actor for Focus {
|
||||
type Form = VoidForm;
|
||||
|
||||
const NAME: &str = "focus";
|
||||
|
||||
fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> { act!(mgr:refresh, cx) }
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_binding::runtime_scope;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::app::LuaForm;
|
||||
use yazi_plugin::LUA;
|
||||
use yazi_shared::data::{Data, Sendable};
|
||||
|
||||
use crate::{Actor, Ctx, lives::Lives};
|
||||
|
||||
pub struct Lua;
|
||||
|
||||
impl Actor for Lua {
|
||||
type Form = LuaForm;
|
||||
|
||||
const NAME: &str = "lua";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let chunk = LUA.load(&*form.code).set_name("anonymous");
|
||||
let result = Lives::scope(cx.core, |_| {
|
||||
runtime_scope!(LUA, "inline", Sendable::value_to_data(&LUA, chunk.eval()?))
|
||||
});
|
||||
succ!(result?);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use mlua::{ObjectLike, Table};
|
||||
use tracing::error;
|
||||
use yazi_actor::lives::Lives;
|
||||
use yazi_binding::runtime_scope;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::app::MouseForm;
|
||||
use yazi_plugin::LUA;
|
||||
use yazi_shared::data::Data;
|
||||
use yazi_term::event::MouseEventKind;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Mouse;
|
||||
|
||||
impl Actor for Mouse {
|
||||
type Form = MouseForm;
|
||||
|
||||
const NAME: &str = "mouse";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() };
|
||||
let area = yazi_binding::elements::Rect::from(size);
|
||||
|
||||
let result = Lives::scope(cx.core, move |_| {
|
||||
runtime_scope!(LUA, "root", {
|
||||
let root = LUA.globals().raw_get::<Table>("Root")?.call_method::<Table>("new", area)?;
|
||||
|
||||
match form.event.kind {
|
||||
MouseEventKind::Down(_) => root.call_method("click", (form.event, false))?,
|
||||
MouseEventKind::Up(_) => root.call_method("click", (form.event, true))?,
|
||||
|
||||
MouseEventKind::ScrollDown => root.call_method("scroll", (form.event, 1))?,
|
||||
MouseEventKind::ScrollUp => root.call_method("scroll", (form.event, -1))?,
|
||||
|
||||
MouseEventKind::ScrollRight => root.call_method("touch", (form.event, 1))?,
|
||||
MouseEventKind::ScrollLeft => root.call_method("touch", (form.event, -1))?,
|
||||
|
||||
MouseEventKind::Moved => root.call_method("move", form.event)?,
|
||||
MouseEventKind::Drag(_) => root.call_method("drag", form.event)?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
if let Err(ref e) = result {
|
||||
error!("{e}");
|
||||
}
|
||||
succ!(result?);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_core::app::PluginMode;
|
||||
use yazi_macro::{act, succ};
|
||||
use yazi_parser::app::PluginForm;
|
||||
use yazi_proxy::AppProxy;
|
||||
use yazi_runner::loader::LOADER;
|
||||
use yazi_scheduler::NotifyProxy;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Plugin;
|
||||
|
||||
impl Actor for Plugin {
|
||||
type Form = PluginForm;
|
||||
|
||||
const NAME: &str = "plugin";
|
||||
|
||||
fn act(cx: &mut Ctx, Self::Form { mut opt }: Self::Form) -> Result<Data> {
|
||||
let mut hits = false;
|
||||
if let Some(chunk) = LOADER.read().get(&*opt.id) {
|
||||
hits = true;
|
||||
opt.mode = opt.mode.auto_then(chunk.sync_entry);
|
||||
}
|
||||
|
||||
if opt.mode == PluginMode::Async {
|
||||
succ!(cx.core.tasks.scheduler.plugin_entry(opt.into()));
|
||||
} else if opt.mode == PluginMode::Sync && hits {
|
||||
return act!(app:plugin_do, cx, opt);
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
match LOADER.ensure(&opt.id, |_| ()).await {
|
||||
Ok(()) => AppProxy::plugin_do(opt),
|
||||
Err(e) => NotifyProxy::push_error("Plugin load failed", e.to_string()),
|
||||
}
|
||||
});
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use mlua::ObjectLike;
|
||||
use scopeguard::defer;
|
||||
use tracing::{error, warn};
|
||||
use yazi_binding::runtime_mut;
|
||||
use yazi_core::app::PluginMode;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::app::PluginForm;
|
||||
use yazi_plugin::LUA;
|
||||
use yazi_runner::{entry::EntryJob, loader::{LOADER, Loader}};
|
||||
use yazi_scheduler::NotifyProxy;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx, lives::Lives};
|
||||
|
||||
pub struct PluginDo;
|
||||
|
||||
impl Actor for PluginDo {
|
||||
type Form = PluginForm;
|
||||
|
||||
const NAME: &str = "plugin_do";
|
||||
|
||||
fn act(cx: &mut Ctx, Self::Form { opt }: Self::Form) -> Result<Data> {
|
||||
let loader = LOADER.read();
|
||||
let Some(chunk) = loader.get(&*opt.id) else {
|
||||
succ!(warn!("plugin `{}` not found", opt.id));
|
||||
};
|
||||
|
||||
if let Err(e) = Loader::compatible_or_error(&opt.id, chunk) {
|
||||
succ!(NotifyProxy::push_error("Incompatible plugin", e.to_string()));
|
||||
}
|
||||
|
||||
if opt.mode.auto_then(chunk.sync_entry) != PluginMode::Sync {
|
||||
succ!(cx.core.tasks.scheduler.plugin_entry(opt.into()));
|
||||
}
|
||||
|
||||
let blocking = runtime_mut!(LUA)?.critical_push(&opt.id, true);
|
||||
defer! { _ = runtime_mut!(LUA).map(|mut r| r.critical_pop(blocking)) }
|
||||
|
||||
let plugin = match LOADER.load_chunk(&LUA, &opt.id, chunk) {
|
||||
Ok(t) => t,
|
||||
Err(e) => succ!(warn!("{e}")),
|
||||
};
|
||||
drop(loader);
|
||||
|
||||
let result = Lives::scope(cx.core, |_| {
|
||||
if let Some(cb) = opt.callback {
|
||||
cb(&LUA, plugin)
|
||||
} else {
|
||||
plugin.call_method("entry", EntryJob { args: opt.args, ..Default::default() })
|
||||
}
|
||||
});
|
||||
if let Err(ref e) = result {
|
||||
error!("Sync plugin `{}` failed: {e}", opt.id);
|
||||
}
|
||||
succ!(result?);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
use std::process;
|
||||
|
||||
use anyhow::Result;
|
||||
use yazi_boot::ARGS;
|
||||
use yazi_fs::engine::{Engine, local::Local};
|
||||
use yazi_parser::app::QuitForm;
|
||||
use yazi_shared::{data::Data, strand::{StrandBuf, StrandLike, ToStrand}};
|
||||
use yazi_tui::Raterm;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Quit;
|
||||
|
||||
impl Actor for Quit {
|
||||
type Form = QuitForm;
|
||||
|
||||
const NAME: &str = "quit";
|
||||
|
||||
fn act(cx: &mut Ctx, Self::Form { opt }: Self::Form) -> Result<Data> {
|
||||
cx.tasks.shutdown();
|
||||
cx.mgr.shutdown();
|
||||
|
||||
futures::executor::block_on(async {
|
||||
_ = futures::join!(
|
||||
yazi_dds::shutdown(),
|
||||
yazi_dds::STATE.drain(),
|
||||
Self::cwd_to_file(cx, opt.no_cwd_file),
|
||||
Self::selected_to_file(opt.selected)
|
||||
);
|
||||
});
|
||||
|
||||
Raterm::stop();
|
||||
process::exit(opt.code);
|
||||
}
|
||||
}
|
||||
|
||||
impl Quit {
|
||||
async fn cwd_to_file(cx: &Ctx<'_>, no: bool) {
|
||||
if let Some(p) = ARGS.cwd_file.as_ref().filter(|_| !no) {
|
||||
let cwd = cx.mgr.cwd().to_strand();
|
||||
Local::regular(p).write(cwd.encoded_bytes()).await.ok();
|
||||
}
|
||||
}
|
||||
|
||||
async fn selected_to_file(selected: Option<StrandBuf>) {
|
||||
if let (Some(s), Some(p)) = (selected, &ARGS.chooser_file) {
|
||||
Local::regular(p).write(s.encoded_bytes()).await.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use mlua::{LuaString, Value};
|
||||
use ratatui_core::layout::Position;
|
||||
use tracing::error;
|
||||
use yazi_actor::lives::Lives;
|
||||
use yazi_config::LAYOUT;
|
||||
use yazi_macro::{render, succ};
|
||||
use yazi_parser::app::ReflowForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Reflow;
|
||||
|
||||
impl Actor for Reflow {
|
||||
type Form = ReflowForm;
|
||||
|
||||
const NAME: &str = "reflow";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() };
|
||||
let mut layout = LAYOUT.get();
|
||||
|
||||
let result = Lives::scope(cx.core, |_| {
|
||||
let comps = (form.reflow)((Position::ORIGIN, size).into())?;
|
||||
|
||||
for v in comps.sequence_values::<Value>() {
|
||||
let Value::Table(t) = v? else {
|
||||
error!("`reflow()` must return a table of components");
|
||||
continue;
|
||||
};
|
||||
|
||||
let id: LuaString = t.get("_id")?;
|
||||
match &*id.as_bytes() {
|
||||
b"current" => layout.current = *t.raw_get::<yazi_binding::elements::Rect>("_area")?,
|
||||
b"preview" => layout.preview = *t.raw_get::<yazi_binding::elements::Rect>("_area")?,
|
||||
b"progress" => layout.progress = *t.raw_get::<yazi_binding::elements::Rect>("_area")?,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
|
||||
if layout != LAYOUT.get() {
|
||||
LAYOUT.set(layout);
|
||||
render!();
|
||||
}
|
||||
|
||||
if let Err(ref e) = result {
|
||||
error!("Failed to `reflow()` the `Root` component:\n{e}");
|
||||
}
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_actor::Ctx;
|
||||
use yazi_macro::act;
|
||||
use yazi_parser::app::ReflowForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::Actor;
|
||||
|
||||
pub struct Resize;
|
||||
|
||||
impl Actor for Resize {
|
||||
type Form = ReflowForm;
|
||||
|
||||
const NAME: &str = "resize";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
act!(app:reflow, cx, form)?;
|
||||
|
||||
cx.current_mut().arrow(0);
|
||||
cx.parent_mut().map(|f| f.arrow(0));
|
||||
cx.current_mut().sync_page(true);
|
||||
|
||||
act!(mgr:peek, cx)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::{act, render, succ};
|
||||
use yazi_parser::app::ReflowForm;
|
||||
use yazi_shared::data::Data;
|
||||
use yazi_tui::Raterm;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Resume;
|
||||
|
||||
impl Actor for Resume {
|
||||
type Form = ReflowForm;
|
||||
|
||||
const NAME: &str = "resume";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
cx.active_mut().preview.reset();
|
||||
|
||||
drop(cx.term.take());
|
||||
*cx.term = Some(Raterm::start()?);
|
||||
|
||||
// While the app resumes, it's possible that the terminal size has changed.
|
||||
// We need to trigger a resize, and render the UI based on the resized area.
|
||||
act!(app:resize, cx, form.reflow)?;
|
||||
|
||||
act!(app:title, cx).ok();
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::VoidForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Stop;
|
||||
|
||||
impl Actor for Stop {
|
||||
type Form = VoidForm;
|
||||
|
||||
const NAME: &str = "stop";
|
||||
|
||||
fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
|
||||
cx.active_mut().preview.reset_image();
|
||||
|
||||
*cx.term = None;
|
||||
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_actor::Ctx;
|
||||
use yazi_config::{THEME, build_flavor};
|
||||
use yazi_emulator::EMULATOR;
|
||||
use yazi_macro::{render, succ};
|
||||
use yazi_parser::VoidForm;
|
||||
use yazi_shared::data::Data;
|
||||
use yazi_shim::serde::Overlay;
|
||||
|
||||
use crate::Actor;
|
||||
|
||||
pub struct Theme;
|
||||
|
||||
impl Actor for Theme {
|
||||
type Form = VoidForm;
|
||||
|
||||
const NAME: &str = "theme";
|
||||
|
||||
fn act(_cx: &mut Ctx, _: Self::Form) -> Result<Data> {
|
||||
THEME.overlay(build_flavor(EMULATOR.light, true)?);
|
||||
yazi_plugin::theme::reset()?;
|
||||
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_actor::Ctx;
|
||||
use yazi_macro::{succ, writef};
|
||||
use yazi_parser::{app::TitleForm, spark::SparkKind};
|
||||
use yazi_shared::{Source, data::Data};
|
||||
use yazi_tty::{TTY, sequence::SetTitle};
|
||||
use yazi_tui::RatermState;
|
||||
|
||||
use crate::Actor;
|
||||
|
||||
pub struct Title;
|
||||
|
||||
impl Actor for Title {
|
||||
type Form = TitleForm;
|
||||
|
||||
const NAME: &str = "title";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let s = form.value.unwrap_or_else(|| format!("Yazi: {}", cx.tab().name()).into());
|
||||
writef!(TTY.writer(), "{}", SetTitle(&s))?;
|
||||
|
||||
yazi_tui::STATE.set(RatermState { title: !s.is_empty(), ..yazi_tui::STATE.get() });
|
||||
succ!()
|
||||
}
|
||||
|
||||
fn hook(cx: &Ctx, _form: &Self::Form) -> Option<SparkKind> {
|
||||
match cx.source() {
|
||||
Source::Ind => Some(SparkKind::IndAppTitle),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_actor::Ctx;
|
||||
use yazi_macro::{act, render, render_partial, succ};
|
||||
use yazi_parser::app::UpdateProgressForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::Actor;
|
||||
|
||||
pub struct UpdateProgress;
|
||||
|
||||
impl Actor for UpdateProgress {
|
||||
type Form = UpdateProgressForm;
|
||||
|
||||
const NAME: &str = "update_progress";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
// Update the progress of all tasks.
|
||||
let tasks = &mut cx.tasks;
|
||||
let progressed = tasks.summary != form.summary;
|
||||
tasks.summary = form.summary;
|
||||
|
||||
// If the task manager is visible, update the snaps with a full render.
|
||||
if tasks.visible {
|
||||
let new = tasks.paginate();
|
||||
if tasks.snaps != new {
|
||||
tasks.snaps = new;
|
||||
act!(tasks:arrow, cx)?;
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
||||
if !progressed {
|
||||
succ!()
|
||||
} else if tasks.summary.total == 0 {
|
||||
succ!(render!())
|
||||
} else {
|
||||
succ!(render_partial!())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::{render, succ};
|
||||
use yazi_parser::ArrowForm;
|
||||
use yazi_shared::data::Data;
|
||||
use yazi_widgets::Scrollable;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Arrow;
|
||||
|
||||
impl Actor for Arrow {
|
||||
type Form = ArrowForm;
|
||||
|
||||
const NAME: &str = "arrow";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
succ!(render!(cx.cmp.scroll(form.step)));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
use std::mem;
|
||||
|
||||
use anyhow::Result;
|
||||
use yazi_macro::{act, render, succ};
|
||||
use yazi_parser::cmp::CloseForm;
|
||||
use yazi_shared::data::Data;
|
||||
use yazi_widgets::input::parser::CompleteOpt;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Close;
|
||||
|
||||
impl Actor for Close {
|
||||
type Form = CloseForm;
|
||||
|
||||
const NAME: &str = "close";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
if form.submit && cx.cmp.visible {
|
||||
Self::flush_last_input(cx)?;
|
||||
}
|
||||
|
||||
let cmp = &mut cx.cmp;
|
||||
if let Some(item) = cmp.selected().filter(|_| form.submit).cloned() {
|
||||
return act!(input:complete, cx, CompleteOpt { name: item.name, is_dir: item.is_dir, ticket: cmp.ticket });
|
||||
}
|
||||
|
||||
cmp.caches.clear();
|
||||
cmp.ticket = Default::default();
|
||||
cmp.handle.take().map(|h| h.abort());
|
||||
succ!(render!(mem::replace(&mut cmp.visible, false)));
|
||||
}
|
||||
}
|
||||
|
||||
impl Close {
|
||||
fn flush_last_input(cx: &mut Ctx) -> Result<Data> {
|
||||
let Some(guard) = cx.input.lock() else { succ!() };
|
||||
if cx.cmp.ticket == guard.ticket.current() {
|
||||
succ!();
|
||||
}
|
||||
|
||||
let before = guard.partition().0.to_owned();
|
||||
drop(guard);
|
||||
|
||||
act!(cmp:trigger, cx, before)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
use std::{mem, ops::ControlFlow};
|
||||
|
||||
use anyhow::Result;
|
||||
use yazi_core::cmp::CmpItem;
|
||||
use yazi_macro::{render, succ};
|
||||
use yazi_parser::cmp::ShowForm;
|
||||
use yazi_shared::{data::Data, path::{DynPath, PathDyn}, strand::StrandLike};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
const LIMIT: usize = 30;
|
||||
|
||||
pub struct Show;
|
||||
|
||||
impl Actor for Show {
|
||||
type Form = ShowForm;
|
||||
|
||||
const NAME: &str = "show";
|
||||
|
||||
fn act(cx: &mut Ctx, Self::Form { opt }: Self::Form) -> Result<Data> {
|
||||
let cmp = &mut cx.cmp;
|
||||
if cmp.ticket != opt.ticket {
|
||||
succ!();
|
||||
}
|
||||
|
||||
if !opt.cache.is_empty() {
|
||||
cmp.caches.insert(opt.cache_name.clone(), opt.cache);
|
||||
}
|
||||
let Some(cache) = cmp.caches.get(&opt.cache_name) else {
|
||||
succ!();
|
||||
};
|
||||
|
||||
cmp.matches = Self::match_candidates(opt.word.dyn_path(), cache);
|
||||
if cmp.matches.is_empty() {
|
||||
succ!(render!(mem::replace(&mut cmp.visible, false)));
|
||||
}
|
||||
|
||||
cmp.offset = 0;
|
||||
cmp.cursor = 0;
|
||||
cmp.visible = true;
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
||||
impl Show {
|
||||
fn match_candidates(word: PathDyn, cache: &[CmpItem]) -> Vec<CmpItem> {
|
||||
let smart = !word.encoded_bytes().iter().any(|&b| b.is_ascii_uppercase());
|
||||
|
||||
let flow = cache.iter().try_fold((Vec::new(), Vec::new()), |(mut exact, mut fuzzy), item| {
|
||||
let starts_with = if smart {
|
||||
item.name.starts_with_ignore_ascii_case(word)
|
||||
} else {
|
||||
item.name.starts_with(word)
|
||||
};
|
||||
|
||||
if starts_with {
|
||||
exact.push(item);
|
||||
if exact.len() >= LIMIT {
|
||||
return ControlFlow::Break((exact, fuzzy));
|
||||
}
|
||||
} else if fuzzy.len() < LIMIT - exact.len() && item.name.contains(word) {
|
||||
// Here we don't break the control flow, since we want more exact matching.
|
||||
fuzzy.push(item)
|
||||
}
|
||||
ControlFlow::Continue((exact, fuzzy))
|
||||
});
|
||||
|
||||
let (exact, fuzzy) = match flow {
|
||||
ControlFlow::Continue(v) => v,
|
||||
ControlFlow::Break(v) => v,
|
||||
};
|
||||
|
||||
let it = fuzzy.into_iter().take(LIMIT - exact.len());
|
||||
exact.into_iter().chain(it).cloned().collect()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
use std::{io, mem};
|
||||
|
||||
use anyhow::Result;
|
||||
use yazi_core::cmp::{CmpItem, CmpOpt};
|
||||
use yazi_fs::{engine::{DirReader, FileHolder}, path::clean_url};
|
||||
use yazi_macro::{act, render, succ};
|
||||
use yazi_parser::cmp::TriggerForm;
|
||||
use yazi_proxy::CmpProxy;
|
||||
use yazi_shared::{AnyAsciiChar, BytePredictor, data::Data, natsort, path::{DynPath, PathBufDyn, PathLike}, spec::Spec, strand::{AsStrand, StrandLike}, url::{UrlBuf, UrlCow, UrlLike}};
|
||||
use yazi_vfs::engine;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Trigger;
|
||||
|
||||
impl Actor for Trigger {
|
||||
type Form = TriggerForm;
|
||||
|
||||
const NAME: &str = "trigger";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
if form.ticket.is_some_and(|t| t != cx.cmp.ticket) {
|
||||
succ!();
|
||||
} else if form.ticket.is_none() {
|
||||
cx.cmp.ticket = cx.input.lock().map(|g| g.ticket.current()).unwrap_or_default();
|
||||
}
|
||||
|
||||
cx.cmp.handle.take().map(|h| h.abort());
|
||||
let Some((parent, word)) = Self::split_url(&form.word) else {
|
||||
return act!(cmp:close, cx, false);
|
||||
};
|
||||
|
||||
let ticket = cx.cmp.ticket;
|
||||
if cx.cmp.caches.contains_key(&parent) {
|
||||
return act!(cmp:show, cx, CmpOpt { cache: vec![], cache_name: parent, word, ticket });
|
||||
}
|
||||
|
||||
cx.cmp.handle = Some(tokio::spawn(async move {
|
||||
let mut dir = engine::read_dir(&parent).await?;
|
||||
let mut cache = vec![];
|
||||
|
||||
// "/" is both a directory separator and the root directory per se
|
||||
// As there's no parent directory for the FS root, it is a special case
|
||||
if parent.loc() == "/" {
|
||||
cache.push(CmpItem { name: Default::default(), is_dir: true });
|
||||
}
|
||||
|
||||
while let Ok(Some(ent)) = dir.next().await {
|
||||
if let Ok(ft) = ent.file_type().await {
|
||||
cache.push(CmpItem { name: ent.name().into_owned(), is_dir: ft.is_dir() });
|
||||
}
|
||||
}
|
||||
|
||||
if !cache.is_empty() {
|
||||
cache
|
||||
.sort_unstable_by(|a, b| natsort(a.name.encoded_bytes(), b.name.encoded_bytes(), true));
|
||||
CmpProxy::show(CmpOpt { cache, cache_name: parent, word, ticket });
|
||||
}
|
||||
|
||||
Ok::<_, io::Error>(())
|
||||
}));
|
||||
|
||||
succ!(render!(mem::replace(&mut cx.cmp.visible, false)));
|
||||
}
|
||||
}
|
||||
|
||||
impl Trigger {
|
||||
fn split_url(s: &str) -> Option<(UrlBuf, PathBufDyn)> {
|
||||
let (spec, path) = Spec::parse(s.as_bytes()).ok()?;
|
||||
if path.is_empty() && !AnyAsciiChar::SEP.predicate(s.bytes().last()?) {
|
||||
return None; // We don't complete a `sftp://test`, but `sftp://test/`
|
||||
}
|
||||
|
||||
// Spec
|
||||
let spec = spec.zeroed();
|
||||
if spec.kind.is_local() && path.as_strand() == "~" {
|
||||
return None; // We don't complete a `~`, but `~/`
|
||||
}
|
||||
|
||||
// Child
|
||||
let child = path.rsplit_pred(AnyAsciiChar::SEP).map_or(path.dyn_path(), |(_, c)| c).to_owned();
|
||||
|
||||
// Parent
|
||||
let url = UrlCow::try_from((spec.clone().zeroed(), path)).ok()?;
|
||||
let abs = if let Some(u) = engine::try_absolute(&url) { u } else { url };
|
||||
let parent = abs.loc().try_strip_suffix(&child).ok()?;
|
||||
|
||||
Some((clean_url(UrlCow::try_from((spec, parent)).ok()?), child))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use yazi_fs::CWD;
|
||||
use yazi_shared::url::UrlLike;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn compare(s: &str, parent: &str, child: &str) {
|
||||
let (mut p, c) = Trigger::split_url(s).unwrap();
|
||||
if let Ok(u) = p.try_strip_prefix(yazi_fs::CWD.load().as_ref()) {
|
||||
p = UrlBuf::Regular(u.as_os().unwrap().into());
|
||||
}
|
||||
assert_eq!((p, c.to_str().unwrap()), (parent.parse().unwrap(), child));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_split() {
|
||||
yazi_shared::init_tests();
|
||||
yazi_config::init_tests();
|
||||
yazi_fs::init();
|
||||
|
||||
assert_eq!(Trigger::split_url(""), None);
|
||||
assert_eq!(Trigger::split_url("sftp://vps"), None);
|
||||
compare(" ", "", " ");
|
||||
|
||||
compare("/", "/", "");
|
||||
compare("//", "/", "");
|
||||
compare("///", "/", "");
|
||||
|
||||
compare("/foo", "/", "foo");
|
||||
compare("//foo", "/", "foo");
|
||||
compare("///foo", "/", "foo");
|
||||
|
||||
compare("/foo/", "/foo/", "");
|
||||
compare("//foo/", "/foo/", "");
|
||||
compare("/foo/bar", "/foo/", "bar");
|
||||
compare("///foo/bar", "/foo/", "bar");
|
||||
|
||||
CWD.set(&"sftp://vps".parse::<UrlBuf>().unwrap(), || {});
|
||||
compare("sftp://vps/a", "sftp://vps/.", "a");
|
||||
compare("sftp://vps//a", "sftp://vps//", "a");
|
||||
compare("test-scope://aws/a", "test-scope://aws/.", "a");
|
||||
compare("test-scope://aws//a", "test-scope://aws//", "a");
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn test_split() {
|
||||
yazi_shared::init_tests();
|
||||
yazi_config::init_tests();
|
||||
yazi_fs::init();
|
||||
compare("foo", "", "foo");
|
||||
|
||||
compare(r"foo\", r"foo\", "");
|
||||
compare(r"foo\bar", r"foo\", "bar");
|
||||
compare(r"foo\bar\", r"foo\bar\", "");
|
||||
|
||||
compare(r"C:\", r"C:\", "");
|
||||
compare(r"C:\foo", r"C:\", "foo");
|
||||
compare(r"C:\foo\", r"C:\foo\", "");
|
||||
compare(r"C:\foo\bar", r"C:\foo\", "bar");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::{render, succ};
|
||||
use yazi_parser::ArrowForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Arrow;
|
||||
|
||||
impl Actor for Arrow {
|
||||
type Form = ArrowForm;
|
||||
|
||||
const NAME: &str = "arrow";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let confirm = &mut cx.core.confirm;
|
||||
|
||||
let area = cx.core.mgr.area(confirm.position);
|
||||
let len = confirm.list.line_count(area.width);
|
||||
|
||||
let old = confirm.offset;
|
||||
confirm.offset = form.step.add(confirm.offset, len, area.height as _, 0, 0);
|
||||
|
||||
succ!(render!(old != confirm.offset));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::{render, succ};
|
||||
use yazi_parser::confirm::CloseForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Close;
|
||||
|
||||
impl Actor for Close {
|
||||
type Form = CloseForm;
|
||||
|
||||
const NAME: &str = "close";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
cx.confirm.token.complete(form.submit);
|
||||
cx.confirm.visible = false;
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::{act, render, succ};
|
||||
use yazi_parser::confirm::ShowForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Show;
|
||||
|
||||
impl Actor for Show {
|
||||
type Form = ShowForm;
|
||||
|
||||
const NAME: &str = "show";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
act!(confirm:close, cx)?;
|
||||
|
||||
let confirm = &mut cx.confirm;
|
||||
confirm.title = form.cfg.title;
|
||||
confirm.body = form.cfg.body;
|
||||
confirm.list = form.cfg.list;
|
||||
|
||||
confirm.position = form.cfg.position;
|
||||
confirm.offset = 0;
|
||||
|
||||
confirm.token = form.token;
|
||||
confirm.visible = true;
|
||||
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use yazi_core::{Core, mgr::Tabs, tab::{Folder, Tab}};
|
||||
use yazi_fs::file::File;
|
||||
use yazi_shared::{Source, event::Action, id::Id, url::UrlBuf};
|
||||
use yazi_tui::Raterm;
|
||||
|
||||
pub struct Ctx<'a> {
|
||||
pub core: &'a mut Core,
|
||||
pub term: &'a mut Option<Raterm>,
|
||||
pub tab: usize,
|
||||
pub level: usize,
|
||||
pub source: Source,
|
||||
#[cfg(debug_assertions)]
|
||||
pub backtrace: Vec<&'static str>,
|
||||
}
|
||||
|
||||
impl Deref for Ctx<'_> {
|
||||
type Target = Core;
|
||||
|
||||
fn deref(&self) -> &Self::Target { self.core }
|
||||
}
|
||||
|
||||
impl DerefMut for Ctx<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target { self.core }
|
||||
}
|
||||
|
||||
impl<'a> Ctx<'a> {
|
||||
pub fn new(action: &Action, core: &'a mut Core, term: &'a mut Option<Raterm>) -> Result<Self> {
|
||||
let tab = if let Ok(id) = action.get::<Id>("tab") {
|
||||
core
|
||||
.mgr
|
||||
.tabs
|
||||
.iter()
|
||||
.position(|t| t.id == id)
|
||||
.ok_or_else(|| anyhow!("Tab with id {id} not found"))?
|
||||
} else {
|
||||
core.mgr.tabs.cursor
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
core,
|
||||
term,
|
||||
tab,
|
||||
level: 0,
|
||||
source: action.source,
|
||||
#[cfg(debug_assertions)]
|
||||
backtrace: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn renew<'b>(cx: &'a mut Ctx<'b>) -> Self {
|
||||
let tab = cx.core.mgr.tabs.cursor;
|
||||
Self {
|
||||
core: cx.core,
|
||||
term: cx.term,
|
||||
tab,
|
||||
level: cx.level,
|
||||
source: cx.source,
|
||||
#[cfg(debug_assertions)]
|
||||
backtrace: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn active(core: &'a mut Core, term: &'a mut Option<Raterm>) -> Self {
|
||||
let tab = core.mgr.tabs.cursor;
|
||||
Self {
|
||||
core,
|
||||
term,
|
||||
tab,
|
||||
level: 0,
|
||||
source: Source::Unknown,
|
||||
#[cfg(debug_assertions)]
|
||||
backtrace: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Ctx<'a> {
|
||||
#[inline]
|
||||
pub fn tabs(&self) -> &Tabs { &self.mgr.tabs }
|
||||
|
||||
#[inline]
|
||||
pub fn tabs_mut(&mut self) -> &mut Tabs { &mut self.mgr.tabs }
|
||||
|
||||
#[inline]
|
||||
pub fn tab(&self) -> &Tab { &self.tabs()[self.tab] }
|
||||
|
||||
#[inline]
|
||||
pub fn tab_mut(&mut self) -> &mut Tab { &mut self.core.mgr.tabs[self.tab] }
|
||||
|
||||
#[inline]
|
||||
pub fn cwd(&self) -> &UrlBuf { self.tab().cwd() }
|
||||
|
||||
#[inline]
|
||||
pub fn parent(&self) -> Option<&Folder> { self.tab().parent.as_ref() }
|
||||
|
||||
#[inline]
|
||||
pub fn parent_mut(&mut self) -> Option<&mut Folder> { self.tab_mut().parent.as_mut() }
|
||||
|
||||
#[inline]
|
||||
pub fn current(&self) -> &Folder { &self.tab().current }
|
||||
|
||||
#[inline]
|
||||
pub fn current_mut(&mut self) -> &mut Folder { &mut self.tab_mut().current }
|
||||
|
||||
#[inline]
|
||||
pub fn hovered(&self) -> Option<&File> { self.tab().hovered() }
|
||||
|
||||
#[inline]
|
||||
pub fn hovered_url(&self) -> Option<&UrlBuf> { self.tab().hovered_url() }
|
||||
|
||||
#[inline]
|
||||
pub fn hovered_folder(&self) -> Option<&Folder> { self.tab().hovered_folder() }
|
||||
|
||||
#[inline]
|
||||
pub fn hovered_folder_mut(&mut self) -> Option<&mut Folder> {
|
||||
self.tab_mut().hovered_folder_mut()
|
||||
}
|
||||
|
||||
pub fn source(&self) -> Source { if self.level != 1 { Source::Ind } else { self.source } }
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
yazi_macro::mod_flat!(preflight);
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use mlua::{ErrorContext, ExternalError, IntoLua, Value};
|
||||
use yazi_binding::runtime_scope;
|
||||
use yazi_dds::LOCAL;
|
||||
use yazi_parser::spark::{Spark, SparkKind};
|
||||
use yazi_plugin::LUA;
|
||||
|
||||
use crate::{Ctx, lives::Lives};
|
||||
|
||||
pub struct Preflight;
|
||||
|
||||
impl Preflight {
|
||||
pub fn act<'a>(cx: &mut Ctx, opt: (SparkKind, Spark<'a>)) -> Result<Spark<'a>> {
|
||||
let kind = opt.0;
|
||||
let Some(handlers) = LOCAL.read().get(kind.into()).filter(|&m| !m.is_empty()).cloned() else {
|
||||
return Ok(opt.1);
|
||||
};
|
||||
|
||||
Ok(Lives::scope(cx.core, |_| {
|
||||
let mut body = opt.1.into_lua(&LUA)?;
|
||||
for (id, cb) in handlers {
|
||||
match runtime_scope!(LUA, &id, cb.call::<Value>(&body)) {
|
||||
Ok(Value::Nil) => {
|
||||
Err(format!("`{kind}` event cancelled by `{id}` plugin on preflight").into_lua_err())?
|
||||
}
|
||||
Ok(v) => body = v,
|
||||
Err(e) => Err(
|
||||
format!("Failed to run `{kind}` event handler in `{id}` plugin: {e}").into_lua_err(),
|
||||
)?,
|
||||
};
|
||||
}
|
||||
|
||||
Spark::from_lua(&LUA, kind, body)
|
||||
.with_context(|e| format!("Unexpected return type from `{kind}` event handlers: {e}"))
|
||||
})?)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::{render, succ};
|
||||
use yazi_parser::ArrowForm;
|
||||
use yazi_shared::data::Data;
|
||||
use yazi_widgets::Scrollable;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Arrow;
|
||||
|
||||
impl Actor for Arrow {
|
||||
type Form = ArrowForm;
|
||||
|
||||
const NAME: &str = "arrow";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
succ!(render!(cx.help.scroll(form.step)));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::{emit, render, succ};
|
||||
use yazi_parser::help::CloseForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Close;
|
||||
|
||||
impl Actor for Close {
|
||||
type Form = CloseForm;
|
||||
|
||||
const NAME: &str = "close";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
let help = &mut cx.help;
|
||||
|
||||
if let Some(chord) = help.bindings.get(help.cursor).filter(|_| opt.submit) {
|
||||
emit!(Seq(chord.to_seq(help.layer)));
|
||||
}
|
||||
|
||||
help.visible = false;
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::{act, render, succ};
|
||||
use yazi_parser::VoidForm;
|
||||
use yazi_shared::data::Data;
|
||||
use yazi_widgets::input::InputMode;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Escape;
|
||||
|
||||
impl Actor for Escape {
|
||||
type Form = VoidForm;
|
||||
|
||||
const NAME: &str = "escape";
|
||||
|
||||
fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
|
||||
if cx.help.input.mode() == InputMode::Normal {
|
||||
return act!(help:close, cx);
|
||||
}
|
||||
|
||||
act!(escape, cx.help.input)?;
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
yazi_macro::mod_flat!(arrow close escape toggle);
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use ratatui_core::layout::Margin;
|
||||
use yazi_config::popup::Help;
|
||||
use yazi_macro::{render, succ};
|
||||
use yazi_parser::help::ToggleForm;
|
||||
use yazi_shared::data::Data;
|
||||
use yazi_widgets::input::Input;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Toggle;
|
||||
|
||||
impl Actor for Toggle {
|
||||
type Form = ToggleForm;
|
||||
|
||||
const NAME: &str = "toggle";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let position = Help::position();
|
||||
let area = cx.mgr.area(position);
|
||||
let input_area = area.inner(Margin::new(1, 1));
|
||||
|
||||
let help = &mut cx.help;
|
||||
help.visible = true;
|
||||
help.layer = form.layer;
|
||||
help.position = position;
|
||||
help.height = area.height;
|
||||
|
||||
help.input = Input::default();
|
||||
help.input.repos(input_area);
|
||||
|
||||
help.keyword.clear();
|
||||
help.offset = 0;
|
||||
help.cursor = 0;
|
||||
help.filter_apply();
|
||||
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_core::input::InputMutGuard;
|
||||
use yazi_macro::{act, render, succ};
|
||||
use yazi_parser::{input::CloseForm, spark::SparkKind};
|
||||
use yazi_shared::{Source, data::Data};
|
||||
use yazi_widgets::input::InputEvent;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Close;
|
||||
|
||||
impl Actor for Close {
|
||||
type Form = CloseForm;
|
||||
|
||||
const NAME: &str = "close";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let Some(mut guard) = cx.input.lock_mut() else {
|
||||
succ!();
|
||||
};
|
||||
|
||||
guard.ticket.next();
|
||||
if let Some(cb) = guard.cb.take() {
|
||||
let value = guard.value().to_owned();
|
||||
cb(if form.submit { InputEvent::Submit(value) } else { InputEvent::Cancel(value) });
|
||||
}
|
||||
|
||||
if form.submit
|
||||
&& let InputMutGuard::Main(input) = guard
|
||||
{
|
||||
input.histories.remember(&input.main.history.name, input.main.value());
|
||||
}
|
||||
|
||||
cx.input.main.visible = false;
|
||||
|
||||
act!(cmp:close, cx)?;
|
||||
succ!(render!());
|
||||
}
|
||||
|
||||
fn hook(cx: &Ctx, _form: &Self::Form) -> Option<SparkKind> {
|
||||
match cx.source() {
|
||||
Source::Key => Some(SparkKind::KeyInputClose),
|
||||
Source::Ind => Some(SparkKind::IndInputClose),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::{act, succ};
|
||||
use yazi_shared::data::Data;
|
||||
use yazi_widgets::input::parser::CompleteOpt;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Complete;
|
||||
|
||||
impl Actor for Complete {
|
||||
type Form = CompleteOpt;
|
||||
|
||||
const NAME: &str = "complete";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let Some(mut guard) = cx.input.lock_mut() else {
|
||||
succ!();
|
||||
};
|
||||
|
||||
if guard.ticket.current() != form.ticket {
|
||||
succ!();
|
||||
}
|
||||
|
||||
act!(complete, guard, form)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::{act, render, succ};
|
||||
use yazi_parser::VoidForm;
|
||||
use yazi_shared::data::Data;
|
||||
use yazi_widgets::input::InputOp;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Escape;
|
||||
|
||||
impl Actor for Escape {
|
||||
type Form = VoidForm;
|
||||
|
||||
const NAME: &str = "escape";
|
||||
|
||||
fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
|
||||
use yazi_widgets::input::InputMode as M;
|
||||
let Some(mut guard) = cx.input.lock_mut() else {
|
||||
succ!();
|
||||
};
|
||||
|
||||
let (mode, op) = (guard.snap().mode, guard.snap().op);
|
||||
act!(escape, guard)?;
|
||||
|
||||
drop(guard);
|
||||
match mode {
|
||||
M::Normal if op == InputOp::None => act!(input:close, cx),
|
||||
M::Insert => act!(cmp:close, cx),
|
||||
M::Normal | M::Replace => Ok(().into()),
|
||||
}?;
|
||||
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
yazi_macro::mod_flat!(close complete escape recall remember show);
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_core::input::InputMutGuard;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::input::RecallForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Recall;
|
||||
|
||||
impl Actor for Recall {
|
||||
type Form = RecallForm;
|
||||
|
||||
const NAME: &str = "recall";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let Some(input) = cx.input.lock_mut() else {
|
||||
succ!();
|
||||
};
|
||||
|
||||
match input {
|
||||
InputMutGuard::Main(input) => {
|
||||
let entries = input.histories.get(&input.main.history.name);
|
||||
input.main.recall(entries, form.step)
|
||||
}
|
||||
InputMutGuard::Alt(input, mut guard) => {
|
||||
let entries = input.histories.get(&guard.history.name);
|
||||
guard.recall(entries, form.step)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_core::input::InputMutGuard;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::VoidForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Remember;
|
||||
|
||||
impl Actor for Remember {
|
||||
type Form = VoidForm;
|
||||
|
||||
const NAME: &str = "remember";
|
||||
|
||||
fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
|
||||
let Some(mut input) = cx.input.lock_mut() else {
|
||||
succ!();
|
||||
};
|
||||
|
||||
match &mut input {
|
||||
InputMutGuard::Main(input) => {
|
||||
input.histories.remember(&input.main.history.name, input.main.value());
|
||||
}
|
||||
InputMutGuard::Alt(input, guard) => {
|
||||
input.histories.remember(&guard.history.name, guard.value());
|
||||
}
|
||||
}
|
||||
|
||||
input.history.take();
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
use std::ops::DerefMut;
|
||||
|
||||
use anyhow::Result;
|
||||
use yazi_config::{THEME, YAZI};
|
||||
use yazi_macro::{act, render, succ};
|
||||
use yazi_parser::input::ShowForm;
|
||||
use yazi_shared::data::Data;
|
||||
use yazi_shim::ratatui::Padable;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Show;
|
||||
|
||||
impl Actor for Show {
|
||||
type Form = ShowForm;
|
||||
|
||||
const NAME: &str = "show";
|
||||
|
||||
fn act(cx: &mut Ctx, Self::Form { mut opt }: Self::Form) -> Result<Data> {
|
||||
act!(input:close, cx)?;
|
||||
|
||||
let area = cx.mgr.area(opt.position).padding(cx.input.padding());
|
||||
let input = &mut cx.input;
|
||||
input.main.name = opt.name.clone();
|
||||
input.main.title = opt.title.clone();
|
||||
input.main.position = opt.position;
|
||||
input.main.visible = true;
|
||||
|
||||
opt.styles = (&THEME.input).into();
|
||||
opt.blinking = YAZI.input.cursor_blink;
|
||||
*input.main.deref_mut() = yazi_widgets::input::Input::new(opt)?;
|
||||
input.main.repos(area);
|
||||
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
extern crate self as yazi_actor;
|
||||
|
||||
yazi_macro::mod_pub!(app cmp confirm core help input lives mgr notify pick spot tasks which);
|
||||
|
||||
yazi_macro::mod_flat!(actor context);
|
||||
|
||||
pub fn init() { lives::init(); }
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use mlua::{AnyUserData, UserData, UserDataMethods};
|
||||
|
||||
use super::{Lives, PtrCell};
|
||||
|
||||
pub(super) struct Behavior {
|
||||
inner: PtrCell<yazi_scheduler::Behavior>,
|
||||
}
|
||||
|
||||
impl Deref for Behavior {
|
||||
type Target = yazi_scheduler::Behavior;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
}
|
||||
|
||||
impl Behavior {
|
||||
pub(super) fn make(inner: &yazi_scheduler::Behavior) -> mlua::Result<AnyUserData> {
|
||||
let inner = PtrCell::from(inner);
|
||||
|
||||
Lives::scoped_userdata(Self { inner })
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for Behavior {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("reset", |_, me, ()| Ok(me.reset()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
use std::{ops::Deref, ptr};
|
||||
|
||||
use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods};
|
||||
use yazi_binding::{Range, style::Style};
|
||||
use yazi_config::THEME;
|
||||
use yazi_fs::file::FileInventory;
|
||||
use yazi_shared::{path::DynPath, url::UrlLike};
|
||||
|
||||
use super::{FILE_CACHE, Lives};
|
||||
use crate::lives::{CoreRef, PtrCell};
|
||||
|
||||
pub(super) struct File {
|
||||
idx: usize,
|
||||
folder: PtrCell<yazi_core::tab::Folder>,
|
||||
tab: PtrCell<yazi_core::tab::Tab>,
|
||||
}
|
||||
|
||||
impl Deref for File {
|
||||
type Target = yazi_fs::file::File;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.folder.entries[self.idx] }
|
||||
}
|
||||
|
||||
impl AsRef<yazi_fs::file::File> for File {
|
||||
fn as_ref(&self) -> &yazi_fs::file::File { self }
|
||||
}
|
||||
|
||||
impl File {
|
||||
pub(super) fn make(
|
||||
idx: usize,
|
||||
folder: &yazi_core::tab::Folder,
|
||||
tab: &yazi_core::tab::Tab,
|
||||
) -> mlua::Result<AnyUserData> {
|
||||
use hashbrown::hash_map::Entry;
|
||||
|
||||
Ok(
|
||||
match unsafe { (*FILE_CACHE.get()).assume_init_mut() }.entry(PtrCell(&folder.entries[idx])) {
|
||||
Entry::Occupied(oe) => oe.into_mut().clone(),
|
||||
Entry::Vacant(ve) => {
|
||||
let ud = Lives::scoped_userdata(Self { idx, folder: folder.into(), tab: tab.into() })?;
|
||||
ve.insert(ud.clone());
|
||||
ud
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_hovered(&self) -> bool { self.idx == self.folder.cursor }
|
||||
}
|
||||
|
||||
impl UserData for File {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
yazi_binding::impl_file_fields!(fields);
|
||||
|
||||
fields.add_field_method_get("idx", |_, me| Ok(me.idx + 1));
|
||||
fields.add_field_method_get("is_hovered", |_, me| Ok(me.is_hovered()));
|
||||
fields.add_field_method_get("in_current", |_, me| Ok(ptr::eq(&*me.folder, &me.tab.current)));
|
||||
fields.add_field_method_get("in_preview", |_, me| {
|
||||
Ok(me.idx == me.folder.cursor && me.tab.hovered().is_some_and(|f| f.url == me.folder.url))
|
||||
});
|
||||
}
|
||||
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
yazi_binding::impl_file_methods!(methods);
|
||||
|
||||
methods.add_method("icon", |lua, me, ()| {
|
||||
yazi_binding::deprecate!(
|
||||
lua,
|
||||
"{}: `File:icon()` is deprecated, use `th.icon:match(file)` instead"
|
||||
);
|
||||
// TODO: use a cache
|
||||
Ok(yazi_config::THEME.icon.matches(me, me.is_hovered()))
|
||||
});
|
||||
methods.add_method("size", |_, me, ()| {
|
||||
Ok(if me.is_dir() {
|
||||
me.folder.entries.sizes.get(&me.entry_key()).copied()
|
||||
} else {
|
||||
Some(me.len)
|
||||
})
|
||||
});
|
||||
methods.add_method("mime", |lua, me, ()| {
|
||||
let core: CoreRef = lua.named_registry_value("cx")?;
|
||||
core.mgr.mimetype.get(&me.url).map(|s| lua.create_string(s)).transpose()
|
||||
});
|
||||
methods.add_method("prefix", |lua, me, ()| {
|
||||
if !me.url.has_trail() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut comp = me.url.try_strip_prefix(me.url.trail()).unwrap_or(me.url.loc()).components();
|
||||
comp.next_back();
|
||||
Some(lua.create_string(comp.dyn_path().encoded_bytes())).transpose()
|
||||
});
|
||||
methods.add_method("style", |lua, me, ()| {
|
||||
let core: CoreRef = lua.named_registry_value("cx")?;
|
||||
let mime = core.mgr.mimetype.get(&me.url).unwrap_or_default();
|
||||
Ok(THEME.filetype.match_style(me, mime).map(Style::from))
|
||||
});
|
||||
methods.add_method("is_yanked", |lua, me, ()| {
|
||||
let core: CoreRef = lua.named_registry_value("cx")?;
|
||||
Ok(if !core.mgr.yanked.contains(&me.url) {
|
||||
0u8
|
||||
} else if core.mgr.yanked.cut {
|
||||
2u8
|
||||
} else {
|
||||
1u8
|
||||
})
|
||||
});
|
||||
methods.add_method("is_marked", |_, me, ()| {
|
||||
let Some(visual) = me.tab.mode.visual() else {
|
||||
return Ok(0u8);
|
||||
};
|
||||
if !visual.contains(me.idx, me.tab.current.cursor, me.folder.entries.len()) {
|
||||
return Ok(0u8);
|
||||
}
|
||||
if me.folder.url != me.tab.current.url {
|
||||
return Ok(0u8);
|
||||
}
|
||||
Ok(if me.tab.mode.is_select() { 1u8 } else { 2u8 })
|
||||
});
|
||||
methods.add_method("is_selected", |_, me, ()| Ok(me.tab.selected.contains(&me.url)));
|
||||
methods.add_method("found", |lua, me, ()| {
|
||||
let core: CoreRef = lua.named_registry_value("cx")?;
|
||||
let Some(finder) = &core.active().finder else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let Some(idx) = finder.matched_idx(&me.folder, me.entry_key()) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
lua.create_sequence_from([idx.into_lua(lua)?, finder.matched.len().into_lua(lua)?]).map(Some)
|
||||
});
|
||||
methods.add_method("highlights", |lua, me, ()| {
|
||||
let core: CoreRef = lua.named_registry_value("cx")?;
|
||||
let Some(finder) = &core.active().finder else {
|
||||
return Ok(None);
|
||||
};
|
||||
if me.folder.url != me.tab.current.url {
|
||||
return Ok(None);
|
||||
}
|
||||
let Some(Some(h)) = me.url.name().map(|s| finder.filter.highlighted(s)) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
lua.create_sequence_from(h.into_iter().map(Range::from)).map(Some)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
inventory::submit! {
|
||||
FileInventory {
|
||||
register: |_| {},
|
||||
borrow: |ud, f| f(&*ud.borrow::<File>()?),
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
use std::ops::{Deref, Range};
|
||||
|
||||
use mlua::{AnyUserData, UserData, UserDataFields};
|
||||
use yazi_config::LAYOUT;
|
||||
use yazi_shim::mlua::UserDataFieldsExt;
|
||||
|
||||
use super::{Entries, File, Lives, PtrCell};
|
||||
|
||||
pub(super) struct Folder {
|
||||
window: Range<usize>,
|
||||
inner: PtrCell<yazi_core::tab::Folder>,
|
||||
tab: PtrCell<yazi_core::tab::Tab>,
|
||||
}
|
||||
|
||||
impl Deref for Folder {
|
||||
type Target = yazi_core::tab::Folder;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
}
|
||||
|
||||
impl Folder {
|
||||
pub(super) fn make(
|
||||
window: Option<Range<usize>>,
|
||||
inner: &yazi_core::tab::Folder,
|
||||
tab: &yazi_core::tab::Tab,
|
||||
) -> mlua::Result<AnyUserData> {
|
||||
let window = match window {
|
||||
Some(w) => w,
|
||||
None => {
|
||||
let limit = LAYOUT.get().preview.height as usize;
|
||||
inner.offset..inner.entries.len().min(inner.offset + limit)
|
||||
}
|
||||
};
|
||||
|
||||
Lives::scoped_userdata(Self { window, inner: inner.into(), tab: tab.into() })
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for Folder {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_cached_field("cwd", |_, me| Ok(me.url.clone()));
|
||||
fields.add_static_field("files", |_, me| Entries::make(0..me.entries.len(), me, &me.tab));
|
||||
fields.add_cached_field("stage", |_, me| Ok(me.stage.clone()));
|
||||
fields.add_static_field("window", |_, me| Entries::make(me.window.clone(), me, &me.tab));
|
||||
|
||||
fields.add_field_method_get("offset", |_, me| Ok(me.offset));
|
||||
fields.add_field_method_get("cursor", |_, me| Ok(me.cursor));
|
||||
fields.add_static_field("hovered", |_, me| {
|
||||
me.hovered().map(|_| File::make(me.cursor, me, &me.tab)).transpose()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use mlua::{AnyUserData, UserData, UserDataFields};
|
||||
use yazi_shim::mlua::UserDataFieldsExt;
|
||||
|
||||
use super::{Lives, PtrCell};
|
||||
use crate::lives::InputAlt;
|
||||
|
||||
pub(super) struct Input {
|
||||
inner: PtrCell<yazi_core::input::Input>,
|
||||
}
|
||||
|
||||
impl Deref for Input {
|
||||
type Target = yazi_core::input::Input;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
}
|
||||
|
||||
impl Input {
|
||||
pub(super) fn make(inner: &yazi_core::input::Input) -> mlua::Result<AnyUserData> {
|
||||
Lives::scoped_userdata(Self { inner: inner.into() })
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for Input {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_cached_field("alt", |_, me| me.alt.as_ref().map(InputAlt::make).transpose());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use mlua::{AnyUserData, UserData, UserDataFields};
|
||||
use yazi_shim::mlua::UserDataFieldsExt;
|
||||
|
||||
use super::{Lives, PtrCell};
|
||||
|
||||
pub(super) struct InputAlt(PtrCell<yazi_core::input::InputAlt>);
|
||||
|
||||
impl Deref for InputAlt {
|
||||
type Target = yazi_core::input::InputAlt;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl InputAlt {
|
||||
pub(super) fn make(inner: &yazi_core::input::InputAlt) -> mlua::Result<AnyUserData> {
|
||||
Lives::scoped_userdata(Self(inner.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for InputAlt {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_cached_field("value", |lua, me| lua.create_string(me.lock().value()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
use std::mem::MaybeUninit;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use mlua::{AnyUserData, UserData};
|
||||
use scopeguard::defer;
|
||||
use tracing::error;
|
||||
use yazi_plugin::LUA;
|
||||
|
||||
use super::{Core, PtrCell};
|
||||
use crate::lives::MutCell;
|
||||
|
||||
pub(super) static TO_DESTROY: MutCell<Vec<AnyUserData>> = MutCell::new(Vec::new());
|
||||
pub(super) static FILE_CACHE: MutCell<
|
||||
MaybeUninit<HashMap<PtrCell<yazi_fs::file::File>, AnyUserData>>,
|
||||
> = MutCell::new(MaybeUninit::uninit());
|
||||
|
||||
pub struct Lives;
|
||||
|
||||
impl Lives {
|
||||
pub fn scope<T, F>(core: &mut yazi_core::Core, f: F) -> mlua::Result<T>
|
||||
where
|
||||
F: FnOnce(&mut yazi_core::Core) -> mlua::Result<T>,
|
||||
{
|
||||
defer! {
|
||||
unsafe {
|
||||
(*FILE_CACHE.get()).assume_init_mut().clear();
|
||||
for ud in (*TO_DESTROY.get()).drain(..) {
|
||||
ud.destroy().expect("failed to destruct scoped userdata");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LUA.set_named_registry_value("cx", Core::make(core)?)?;
|
||||
LUA.globals().raw_set("cx", Core::make(core)?)?;
|
||||
let result = f(core);
|
||||
|
||||
if let Err(ref e) = result {
|
||||
error!("{e}");
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub(crate) fn scoped_userdata<T>(data: T) -> mlua::Result<AnyUserData>
|
||||
where
|
||||
T: UserData + 'static,
|
||||
{
|
||||
let ud = LUA.create_userdata(data)?;
|
||||
unsafe { &mut *TO_DESTROY.get() }.push(ud.clone());
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
yazi_macro::mod_flat!(behavior core entries file filter finder folder input input_alt lives mode mut_cell preference preview ptr selected tab tabs task tasks which yanked);
|
||||
|
||||
pub(super) fn init() {
|
||||
unsafe { FILE_CACHE.get().write(std::mem::MaybeUninit::new(<_>::default())) };
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
use std::cell::UnsafeCell;
|
||||
|
||||
pub(super) struct MutCell<T>(UnsafeCell<T>);
|
||||
|
||||
unsafe impl<T> Sync for MutCell<T> {}
|
||||
|
||||
impl<T> MutCell<T> {
|
||||
pub(super) const fn new(value: T) -> Self { Self(UnsafeCell::new(value)) }
|
||||
|
||||
pub(super) fn get(&self) -> *mut T { self.0.get() }
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
use mlua::AnyUserData;
|
||||
|
||||
use super::{Lives, PtrCell};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) struct Selected;
|
||||
|
||||
impl Selected {
|
||||
pub(super) fn make(inner: &yazi_core::tab::Selected) -> mlua::Result<AnyUserData> {
|
||||
let inner = PtrCell::from(inner);
|
||||
|
||||
Lives::scoped_userdata(yazi_binding::Iter::new(
|
||||
inner.as_static().files().cloned(),
|
||||
Some(inner.len()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
use std::{borrow::Cow, ops::Deref};
|
||||
|
||||
use mlua::{AnyUserData, UserData, UserDataFields, UserDataMethods};
|
||||
use yazi_shared::url::UrlRef;
|
||||
use yazi_shim::mlua::UserDataFieldsExt;
|
||||
|
||||
use super::{Finder, Folder, Lives, Mode, Preference, Preview, PtrCell, Selected};
|
||||
|
||||
pub(super) struct Tab {
|
||||
inner: PtrCell<yazi_core::tab::Tab>,
|
||||
}
|
||||
|
||||
impl Deref for Tab {
|
||||
type Target = yazi_core::tab::Tab;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
pub(super) fn make(inner: &yazi_core::tab::Tab) -> mlua::Result<AnyUserData> {
|
||||
Lives::scoped_userdata(Self { inner: inner.into() })
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for Tab {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("id", |_, me| Ok(me.id));
|
||||
fields.add_cached_field("name", |lua, me| match me.name() {
|
||||
Cow::Borrowed(s) => lua.create_string(s),
|
||||
Cow::Owned(s) => lua.create_external_string(s),
|
||||
});
|
||||
|
||||
fields.add_static_field("mode", |_, me| Mode::make(&me.mode));
|
||||
fields.add_static_field("pref", |_, me| Preference::make(&me.pref));
|
||||
fields.add_static_field("current", |_, me| Folder::make(None, &me.current, me));
|
||||
fields.add_static_field("parent", |_, me| {
|
||||
me.parent.as_ref().map(|f| Folder::make(None, f, me)).transpose()
|
||||
});
|
||||
|
||||
fields.add_static_field("selected", |_, me| Selected::make(&me.selected));
|
||||
|
||||
fields.add_static_field("preview", |_, me| Preview::make(me));
|
||||
fields.add_static_field("finder", |_, me| me.finder.as_ref().map(Finder::make).transpose());
|
||||
}
|
||||
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("history", |_, me, url: UrlRef| {
|
||||
me.history.get(url.as_ref()).map(|f| Folder::make(None, f, me)).transpose()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields};
|
||||
use yazi_scheduler::Progress;
|
||||
use yazi_shim::mlua::{SER_OPT, UserDataFieldsExt};
|
||||
|
||||
use super::{Lives, PtrCell};
|
||||
|
||||
pub(super) struct TaskSnap {
|
||||
inner: PtrCell<yazi_scheduler::TaskSnap>,
|
||||
}
|
||||
|
||||
impl Deref for TaskSnap {
|
||||
type Target = yazi_scheduler::TaskSnap;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
}
|
||||
|
||||
impl TaskSnap {
|
||||
pub(super) fn make(inner: &yazi_scheduler::TaskSnap) -> mlua::Result<AnyUserData> {
|
||||
Lives::scoped_userdata(Self { inner: inner.into() })
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for TaskSnap {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_cached_field("title", |lua, me| lua.create_string(&me.title));
|
||||
fields.add_cached_field("prog", |lua, me| lua.to_value_with(&me.prog, SER_OPT));
|
||||
|
||||
fields.add_field_method_get("running", |_, me| Ok(me.prog.running()));
|
||||
fields.add_field_method_get("cooked", |_, me| Ok(me.prog.cooked()));
|
||||
fields.add_field_method_get("success", |_, me| Ok(me.prog.success()));
|
||||
fields.add_field_method_get("failed", |_, me| Ok(me.prog.failed()));
|
||||
fields.add_field_method_get("percent", |_, me| Ok(me.prog.percent()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields};
|
||||
use yazi_shim::mlua::{SER_OPT, UserDataFieldsExt};
|
||||
|
||||
use super::{Lives, PtrCell};
|
||||
use crate::lives::{Behavior, TaskSnap};
|
||||
|
||||
pub(super) struct Tasks {
|
||||
inner: PtrCell<yazi_core::tasks::Tasks>,
|
||||
}
|
||||
|
||||
impl Deref for Tasks {
|
||||
type Target = yazi_core::tasks::Tasks;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
}
|
||||
|
||||
impl Tasks {
|
||||
pub(super) fn make(inner: &yazi_core::tasks::Tasks) -> mlua::Result<AnyUserData> {
|
||||
Lives::scoped_userdata(Self { inner: inner.into() })
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for Tasks {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("cursor", |_, me| Ok(me.cursor));
|
||||
|
||||
fields.add_static_field("behavior", |_, me| Behavior::make(&me.scheduler.behavior));
|
||||
|
||||
fields.add_static_field("snaps", |lua, me| {
|
||||
let tbl = lua.create_table_with_capacity(me.snaps.len(), 0)?;
|
||||
for snap in &me.snaps {
|
||||
tbl.raw_push(TaskSnap::make(snap)?)?;
|
||||
}
|
||||
Ok(tbl)
|
||||
});
|
||||
|
||||
fields.add_cached_field("summary", |lua, me| lua.to_value_with(&me.summary, SER_OPT));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use mlua::{AnyUserData, UserData, UserDataFields};
|
||||
use yazi_shim::mlua::UserDataFieldsExt;
|
||||
|
||||
use super::{Lives, PtrCell};
|
||||
|
||||
pub(super) struct Which {
|
||||
inner: PtrCell<yazi_core::which::Which>,
|
||||
}
|
||||
|
||||
impl Deref for Which {
|
||||
type Target = yazi_core::which::Which;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
}
|
||||
|
||||
impl Which {
|
||||
pub(super) fn make(inner: &yazi_core::which::Which) -> mlua::Result<AnyUserData> {
|
||||
Lives::scoped_userdata(Self { inner: inner.into() })
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for Which {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_cached_field("tx", |_, me| Ok(me.tx.clone().map(yazi_binding::MpscUnboundedTx)));
|
||||
fields.add_cached_field("cands", |lua, me| {
|
||||
lua.create_sequence_from(me.inner.cands.iter().cloned())
|
||||
});
|
||||
fields.add_field_method_get("times", |_, me| Ok(me.inner.times));
|
||||
|
||||
fields.add_field_method_get("active", |_, me| Ok(me.inner.active));
|
||||
fields.add_field_method_get("silent", |_, me| Ok(me.inner.silent));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::{act, render, succ};
|
||||
use yazi_parser::ArrowForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Arrow;
|
||||
|
||||
impl Actor for Arrow {
|
||||
type Form = ArrowForm;
|
||||
|
||||
const NAME: &str = "arrow";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let tab = cx.tab_mut();
|
||||
let old = tab.current.cursor;
|
||||
if !tab.current.arrow(form.step) {
|
||||
succ!();
|
||||
}
|
||||
|
||||
// Retrace
|
||||
tab.current.retrace();
|
||||
|
||||
// Visual selection
|
||||
if let Some(visual) = tab.mode.visual_mut() {
|
||||
visual.arrow(form.step, old, tab.current.cursor);
|
||||
}
|
||||
|
||||
act!(mgr:hover, cx)?;
|
||||
act!(mgr:peek, cx)?;
|
||||
act!(mgr:watch, cx)?;
|
||||
|
||||
cx.tasks.scheduler.behavior.reset();
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_core::mgr::CdSource;
|
||||
use yazi_macro::{act, succ};
|
||||
use yazi_parser::VoidForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Back;
|
||||
|
||||
impl Actor for Back {
|
||||
type Form = VoidForm;
|
||||
|
||||
const NAME: &str = "back";
|
||||
|
||||
fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
|
||||
if let Some(u) = cx.tab_mut().backstack.shift_backward().cloned() {
|
||||
act!(mgr:cd, cx, (u, CdSource::Back))?;
|
||||
}
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,175 +0,0 @@
|
|||
use std::{fmt::{self, Display}, io::{self, Read, Write}, path::{MAIN_SEPARATOR, Path}};
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use scopeguard::defer;
|
||||
use yazi_binding::Permit;
|
||||
use yazi_config::{YAZI, opener::OpenerRuleArc};
|
||||
use yazi_fs::{FilesOp, Splatter, engine::{Engine, local::Local}};
|
||||
use yazi_macro::{succ, writef};
|
||||
use yazi_parser::VoidForm;
|
||||
use yazi_proxy::TasksProxy;
|
||||
use yazi_scheduler::{AppProxy, NotifyProxy, process::ShellOpt};
|
||||
use yazi_shared::{data::Data, strand::Strand, url::{UrlBuf, UrlLike}};
|
||||
use yazi_shim::path::CROSS_SEPARATOR;
|
||||
use yazi_term::YIELD_TO_SUBPROCESS;
|
||||
use yazi_tty::{TTY, sequence::EraseScreen};
|
||||
use yazi_vfs::engine;
|
||||
use yazi_watcher::WATCHER;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
pub struct BulkCreate;
|
||||
impl Actor for BulkCreate {
|
||||
type Form = VoidForm;
|
||||
|
||||
const NAME: &str = "bulk_create";
|
||||
|
||||
fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
|
||||
let Some(opener) = Self::opener() else {
|
||||
succ!(NotifyProxy::push_warn("Bulk create", "No text opener found"));
|
||||
};
|
||||
|
||||
let cwd = cx.cwd().clone();
|
||||
tokio::spawn(async move {
|
||||
let tmp = YAZI.preview.tmpfile("bulk-create");
|
||||
let file = engine::create_new(&tmp).await?.file().await?;
|
||||
|
||||
defer! {
|
||||
let tmp = tmp.clone();
|
||||
tokio::spawn(async move {
|
||||
Local::regular(&tmp).remove_file().await
|
||||
});
|
||||
}
|
||||
|
||||
TasksProxy::process_exec(ShellOpt {
|
||||
cwd: cwd.clone(),
|
||||
cmd: Splatter::new(&[file]).splat(&opener.run),
|
||||
block: opener.block,
|
||||
orphan: opener.orphan,
|
||||
})
|
||||
.await;
|
||||
|
||||
let _permit = Permit::new(YIELD_TO_SUBPROCESS.acquire().await.unwrap(), AppProxy::resume());
|
||||
AppProxy::stop().await;
|
||||
|
||||
let content = Local::regular(&tmp).read_to_string().await?;
|
||||
Self::r#do(cwd, content.lines().filter_map(Entry::parse).collect()).await
|
||||
});
|
||||
succ!()
|
||||
}
|
||||
}
|
||||
|
||||
impl BulkCreate {
|
||||
async fn r#do(cwd: UrlBuf, todo: Vec<Entry<'_>>) -> Result<()> {
|
||||
writef!(TTY.writer(), "{EraseScreen}\n")?;
|
||||
if todo.is_empty() {
|
||||
return Ok(());
|
||||
} else if !Self::ask_continue(&todo, None)? {
|
||||
return Ok(()); // TODO: support `bulk_exit`?
|
||||
}
|
||||
|
||||
let _permit = WATCHER.acquire().await.unwrap();
|
||||
let (mut failed, mut succeeded) = (vec![], Vec::with_capacity(todo.len()));
|
||||
for entry in todo {
|
||||
let Ok(dist) = cwd.try_join(entry.path) else {
|
||||
failed.push((entry, anyhow!("Invalid path")));
|
||||
continue;
|
||||
};
|
||||
|
||||
let result: io::Result<()> = if entry.is_dir {
|
||||
engine::create_dir_all(&dist).await
|
||||
} else if let Some(parent) = dist.parent() {
|
||||
engine::create_dir_all(parent).await.ok();
|
||||
engine::create_new(&dist).await.map(|_| ())
|
||||
} else {
|
||||
Err(io::Error::other("No parent directory"))
|
||||
};
|
||||
|
||||
if let Err(e) = result {
|
||||
failed.push((entry, e.into()));
|
||||
} else if let Ok(f) = engine::file(dist).await {
|
||||
succeeded.push(f);
|
||||
} else {
|
||||
failed.push((entry, anyhow!("Failed to retrieve file info")));
|
||||
}
|
||||
}
|
||||
|
||||
if !succeeded.is_empty() {
|
||||
// err!(Pubsub::pub_after_bulk_create(it)); // FIXME
|
||||
FilesOp::create(succeeded);
|
||||
}
|
||||
drop(_permit);
|
||||
|
||||
if !failed.is_empty() {
|
||||
Self::output_failed(failed).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn opener() -> Option<OpenerRuleArc> {
|
||||
YAZI
|
||||
.open
|
||||
.match_dummy(Path::new("bulk-create.txt"), "text/plain")
|
||||
.and_then(|r| YAZI.opener.block(&r))
|
||||
}
|
||||
|
||||
fn ask_continue(todo: &[Entry], decision: Option<bool>) -> Result<bool> {
|
||||
if let Some(decision) = decision {
|
||||
return Ok(decision);
|
||||
}
|
||||
|
||||
{
|
||||
let mut w = TTY.lockout();
|
||||
for entry in todo {
|
||||
writeln!(w, "{entry}")?;
|
||||
}
|
||||
write!(w, "Continue to create? (y/N): ")?;
|
||||
w.flush()?;
|
||||
}
|
||||
|
||||
let mut buf = [0; 10];
|
||||
_ = TTY.reader().read(&mut buf)?;
|
||||
Ok(buf[0] == b'y' || buf[0] == b'Y')
|
||||
}
|
||||
|
||||
async fn output_failed(failed: Vec<(Entry<'_>, anyhow::Error)>) -> Result<()> {
|
||||
let mut stdout = TTY.lockout();
|
||||
writeln!(stdout, "{EraseScreen}")?;
|
||||
|
||||
writeln!(stdout, "Failed to create:")?;
|
||||
for (entry, err) in failed {
|
||||
writeln!(stdout, "{entry}: {err}")?;
|
||||
}
|
||||
writeln!(stdout, "\nPress ENTER to exit")?;
|
||||
|
||||
stdout.flush()?;
|
||||
TTY.reader().read_exact(&mut [0])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// --- Entry
|
||||
struct Entry<'a> {
|
||||
path: Strand<'a>,
|
||||
is_dir: bool,
|
||||
}
|
||||
|
||||
impl<'a> Entry<'a> {
|
||||
fn parse(s: &'a str) -> Option<Self> {
|
||||
let (path, is_dir) = match s.strip_suffix(CROSS_SEPARATOR) {
|
||||
Some(p) => (p, true),
|
||||
None => (s, false),
|
||||
};
|
||||
|
||||
(!path.is_empty()).then_some(Self { path: path.into(), is_dir })
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Entry<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_dir {
|
||||
write!(f, "{}{MAIN_SEPARATOR}", self.path.display())
|
||||
} else {
|
||||
self.path.display().fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::mgr::BulkExitForm;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct BulkExit;
|
||||
|
||||
impl Actor for BulkExit {
|
||||
type Form = BulkExitForm;
|
||||
|
||||
const NAME: &str = "bulk_exit";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
cx.mgr.batcher.decide(form.target, form.accept);
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,308 +0,0 @@
|
|||
use std::{hash::Hash, io::{Read, Write}, ops::Deref, path::Path};
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use hashbrown::HashMap;
|
||||
use scopeguard::defer;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use yazi_binding::Permit;
|
||||
use yazi_config::{YAZI, opener::OpenerRuleArc};
|
||||
use yazi_dds::Pubsub;
|
||||
use yazi_fs::{FilesOp, Splatter, engine::{Engine, FileBuilder, local::Local}, max_common_root, path::skip_url};
|
||||
use yazi_macro::{err, succ, writef};
|
||||
use yazi_parser::VoidForm;
|
||||
use yazi_proxy::TasksProxy;
|
||||
use yazi_scheduler::{AppProxy, NotifyProxy, process::ShellOpt};
|
||||
use yazi_shared::{data::Data, path::PathDyn, strand::{AsStrand, AsStrandJoin, Strand, StrandBuf, StrandLike}, url::{AsUrl, UrlBuf, UrlLike}};
|
||||
use yazi_term::YIELD_TO_SUBPROCESS;
|
||||
use yazi_tty::{TTY, sequence::EraseScreen};
|
||||
use yazi_vfs::{engine::{self, Demand}, maybe_exists};
|
||||
use yazi_watcher::WATCHER;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct BulkRename;
|
||||
|
||||
impl Actor for BulkRename {
|
||||
type Form = VoidForm;
|
||||
|
||||
const NAME: &str = "bulk_rename";
|
||||
|
||||
fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
|
||||
let Some(opener) = Self::opener() else {
|
||||
succ!(NotifyProxy::push_warn("Bulk rename", "No text opener found"));
|
||||
};
|
||||
|
||||
let selected: Vec<_> = cx.tab().selected_or_hovered_urls().cloned().collect();
|
||||
if selected.is_empty() {
|
||||
succ!(NotifyProxy::push_warn("Bulk rename", "No files selected"));
|
||||
}
|
||||
|
||||
let root = max_common_root(&selected);
|
||||
let old: Vec<_> =
|
||||
selected.iter().enumerate().map(|(i, u)| Tuple::new(i, skip_url(u, root))).collect();
|
||||
|
||||
let cwd = cx.cwd().clone();
|
||||
let batcher = cx.core.mgr.batcher.clone();
|
||||
tokio::spawn(async move {
|
||||
let tmp = YAZI.preview.tmpfile("bulk-rename");
|
||||
|
||||
let mut rw = Demand::default().write(true).create_new(true).open(&tmp).await?;
|
||||
rw.write_all(old.join(Strand::Utf8("\n")).encoded_bytes()).await?;
|
||||
|
||||
defer! {
|
||||
let tmp = tmp.clone();
|
||||
batcher.drain(&tmp);
|
||||
tokio::spawn(async move {
|
||||
Local::regular(&tmp).remove_file().await
|
||||
});
|
||||
}
|
||||
|
||||
batcher.prime(&tmp);
|
||||
TasksProxy::process_exec(ShellOpt {
|
||||
cwd,
|
||||
cmd: Splatter::new(&[rw.into_file().await?]).splat(&opener.run),
|
||||
block: opener.block,
|
||||
orphan: opener.orphan,
|
||||
})
|
||||
.await;
|
||||
|
||||
let _permit = Permit::new(YIELD_TO_SUBPROCESS.acquire().await.unwrap(), AppProxy::resume());
|
||||
AppProxy::stop().await;
|
||||
|
||||
let new: Vec<_> = Local::regular(&tmp)
|
||||
.read_to_string()
|
||||
.await?
|
||||
.lines()
|
||||
.take(old.len())
|
||||
.enumerate()
|
||||
.map(|(i, s)| Tuple::new(i, s))
|
||||
.collect();
|
||||
|
||||
let decision = batcher.drain(&tmp);
|
||||
Self::r#do(root, old, new, selected, decision).await
|
||||
});
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
||||
impl BulkRename {
|
||||
async fn r#do(
|
||||
root: usize,
|
||||
old: Vec<Tuple>,
|
||||
new: Vec<Tuple>,
|
||||
selected: Vec<UrlBuf>,
|
||||
decision: Option<bool>,
|
||||
) -> Result<()> {
|
||||
writef!(TTY.writer(), "{EraseScreen}\n")?;
|
||||
if old.len() != new.len() {
|
||||
#[rustfmt::skip]
|
||||
writef!(TTY.writer(), "Number of new and old file names mismatch (New: {}, Old: {}).\nPress <Enter> to exit...", new.len(), old.len())?;
|
||||
|
||||
TTY.reader().read_exact(&mut [0])?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (old, new) = old.into_iter().zip(new).filter(|(o, n)| o != n).unzip();
|
||||
let todo = Self::prioritized_paths(old, new);
|
||||
if todo.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !Self::ask_continue(&todo, decision)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let permit = WATCHER.acquire().await.unwrap();
|
||||
let (mut failed, mut succeeded) = (Vec::new(), HashMap::with_capacity(todo.len()));
|
||||
for (o, n) in todo {
|
||||
let (Ok(old), Ok(new)) =
|
||||
(Self::replace_url(&selected[o.0], root, &o), Self::replace_url(&selected[n.0], root, &n))
|
||||
else {
|
||||
failed.push((o, n, anyhow!("Invalid new or old file name")));
|
||||
continue;
|
||||
};
|
||||
|
||||
if maybe_exists(&new).await && !engine::must_identical(&old, &new).await {
|
||||
failed.push((o, n, anyhow!("Destination already exists")));
|
||||
} else if let Err(e) = engine::rename(&old, &new).await {
|
||||
failed.push((o, n, e.into()));
|
||||
} else if let Ok(f) = engine::file(new).await {
|
||||
succeeded.insert(old, f);
|
||||
} else {
|
||||
failed.push((o, n, anyhow!("Failed to retrieve file info")));
|
||||
}
|
||||
}
|
||||
|
||||
if !succeeded.is_empty() {
|
||||
let it = succeeded.iter().map(|(o, n)| (o.as_url(), n.url.as_url()));
|
||||
err!(Pubsub::pub_after_bulk_rename(it));
|
||||
FilesOp::rename(succeeded);
|
||||
}
|
||||
drop(permit);
|
||||
|
||||
if !failed.is_empty() {
|
||||
Self::output_failed(failed).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn opener() -> Option<OpenerRuleArc> {
|
||||
YAZI
|
||||
.open
|
||||
.match_dummy(Path::new("bulk-rename.txt"), "text/plain")
|
||||
.and_then(|r| YAZI.opener.block(&r))
|
||||
}
|
||||
|
||||
fn replace_url(url: &UrlBuf, take: usize, rep: &StrandBuf) -> Result<UrlBuf> {
|
||||
Ok(url.try_replace(take, PathDyn::with(url.kind(), rep)?)?.into_owned())
|
||||
}
|
||||
|
||||
fn ask_continue(todo: &[(Tuple, Tuple)], decision: Option<bool>) -> Result<bool> {
|
||||
if let Some(decision) = decision {
|
||||
return Ok(decision);
|
||||
}
|
||||
|
||||
{
|
||||
let mut w = TTY.lockout();
|
||||
for (old, new) in todo {
|
||||
writeln!(w, "{} -> {}", old.display(), new.display())?;
|
||||
}
|
||||
write!(w, "Continue to rename? (y/N): ")?;
|
||||
w.flush()?;
|
||||
}
|
||||
|
||||
let mut buf = [0; 10];
|
||||
_ = TTY.reader().read(&mut buf)?;
|
||||
Ok(buf[0] == b'y' || buf[0] == b'Y')
|
||||
}
|
||||
|
||||
async fn output_failed(failed: Vec<(Tuple, Tuple, anyhow::Error)>) -> Result<()> {
|
||||
let mut stdout = TTY.lockout();
|
||||
writeln!(stdout, "{EraseScreen}")?;
|
||||
|
||||
writeln!(stdout, "Failed to rename:")?;
|
||||
for (old, new, err) in failed {
|
||||
writeln!(stdout, "{} -> {}: {err}", old.display(), new.display())?;
|
||||
}
|
||||
writeln!(stdout, "\nPress ENTER to exit")?;
|
||||
|
||||
stdout.flush()?;
|
||||
TTY.reader().read_exact(&mut [0])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prioritized_paths(old: Vec<Tuple>, new: Vec<Tuple>) -> Vec<(Tuple, Tuple)> {
|
||||
let orders: HashMap<_, _> = old.iter().enumerate().map(|(i, t)| (t, i)).collect();
|
||||
let mut incomes: HashMap<_, _> = old.iter().map(|t| (t, false)).collect();
|
||||
let mut todos: HashMap<_, _> = old
|
||||
.iter()
|
||||
.zip(new)
|
||||
.map(|(o, n)| {
|
||||
incomes.get_mut(&n).map(|b| *b = true);
|
||||
(o, n)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut sorted = Vec::with_capacity(old.len());
|
||||
while !todos.is_empty() {
|
||||
// Paths that are non-incomes and don't need to be prioritized in this round
|
||||
let mut outcomes: Vec<_> = incomes.iter().filter(|&(_, b)| !b).map(|(&t, _)| t).collect();
|
||||
outcomes.sort_unstable_by(|a, b| orders[b].cmp(&orders[a]));
|
||||
|
||||
// If there're no outcomes, it means there are cycles in the renaming
|
||||
if outcomes.is_empty() {
|
||||
let mut remain: Vec<_> = todos.into_iter().map(|(o, n)| (o.clone(), n)).collect();
|
||||
remain.sort_unstable_by(|(a, _), (b, _)| orders[a].cmp(&orders[b]));
|
||||
sorted.reverse();
|
||||
sorted.extend(remain);
|
||||
return sorted;
|
||||
}
|
||||
|
||||
for old in outcomes {
|
||||
let Some(new) = todos.remove(old) else { unreachable!() };
|
||||
incomes.remove(&old);
|
||||
incomes.get_mut(&new).map(|b| *b = false);
|
||||
sorted.push((old.clone(), new));
|
||||
}
|
||||
}
|
||||
sorted.reverse();
|
||||
sorted
|
||||
}
|
||||
}
|
||||
|
||||
// --- Tuple
|
||||
#[derive(Clone, Debug)]
|
||||
struct Tuple(usize, StrandBuf);
|
||||
|
||||
impl Deref for Tuple {
|
||||
type Target = StrandBuf;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.1 }
|
||||
}
|
||||
|
||||
impl PartialEq for Tuple {
|
||||
fn eq(&self, other: &Self) -> bool { self.1 == other.1 }
|
||||
}
|
||||
|
||||
impl Eq for Tuple {}
|
||||
|
||||
impl Hash for Tuple {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.1.hash(state); }
|
||||
}
|
||||
|
||||
impl AsStrand for &Tuple {
|
||||
fn as_strand(&self) -> Strand<'_> { self.1.as_strand() }
|
||||
}
|
||||
|
||||
impl Tuple {
|
||||
fn new(index: usize, inner: impl Into<StrandBuf>) -> Self { Self(index, inner.into()) }
|
||||
}
|
||||
|
||||
// --- Tests
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_sort() {
|
||||
fn cmp(input: &[(&str, &str)], expected: &[(&str, &str)]) {
|
||||
let sorted = BulkRename::prioritized_paths(
|
||||
input.iter().map(|&(o, _)| Tuple::new(0, o)).collect(),
|
||||
input.iter().map(|&(_, n)| Tuple::new(0, n)).collect(),
|
||||
);
|
||||
let sorted: Vec<_> =
|
||||
sorted.iter().map(|(o, n)| (o.to_str().unwrap(), n.to_str().unwrap())).collect();
|
||||
assert_eq!(sorted, expected);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
cmp(
|
||||
&[("2", "3"), ("1", "2"), ("3", "4")],
|
||||
&[("3", "4"), ("2", "3"), ("1", "2")]
|
||||
);
|
||||
|
||||
#[rustfmt::skip]
|
||||
cmp(
|
||||
&[("1", "3"), ("2", "3"), ("3", "4")],
|
||||
&[("3", "4"), ("1", "3"), ("2", "3")]
|
||||
);
|
||||
|
||||
#[rustfmt::skip]
|
||||
cmp(
|
||||
&[("2", "1"), ("1", "2")],
|
||||
&[("2", "1"), ("1", "2")]
|
||||
);
|
||||
|
||||
#[rustfmt::skip]
|
||||
cmp(
|
||||
&[("3", "2"), ("2", "1"), ("1", "3"), ("a", "b"), ("b", "c")],
|
||||
&[("b", "c"), ("a", "b"), ("3", "2"), ("2", "1"), ("1", "3")]
|
||||
);
|
||||
|
||||
#[rustfmt::skip]
|
||||
cmp(
|
||||
&[("b", "b_"), ("a", "a_"), ("c", "c_")],
|
||||
&[("b", "b_"), ("a", "a_"), ("c", "c_")],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
use std::{mem, time::Duration};
|
||||
|
||||
use anyhow::Result;
|
||||
use tokio::pin;
|
||||
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
||||
use yazi_config::YAZI;
|
||||
use yazi_core::mgr::CdSource;
|
||||
use yazi_dds::Pubsub;
|
||||
use yazi_fs::{FilesOp, path::{clean_url, expand_url}};
|
||||
use yazi_macro::{act, err, input, render, succ};
|
||||
use yazi_parser::mgr::CdForm;
|
||||
use yazi_proxy::{CmpProxy, MgrProxy};
|
||||
use yazi_shared::{Debounce, data::Data, url::{AsUrl, UrlBuf, UrlLike}};
|
||||
use yazi_vfs::engine;
|
||||
use yazi_widgets::input::InputEvent;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Cd;
|
||||
|
||||
impl Actor for Cd {
|
||||
type Form = CdForm;
|
||||
|
||||
const NAME: &str = "cd";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
act!(mgr:escape_visual, cx)?;
|
||||
if form.interactive {
|
||||
return Self::cd_interactive(cx);
|
||||
} else if form.target == *cx.cwd() {
|
||||
succ!();
|
||||
}
|
||||
|
||||
// Stash first so it's possible to access the original cwd in hooks
|
||||
act!(mgr:stash, cx, &form).ok();
|
||||
|
||||
// Take parent to history
|
||||
let tab = cx.tab_mut();
|
||||
if let Some(t) = tab.parent.take() {
|
||||
tab.history.insert(t.url.clone(), t);
|
||||
}
|
||||
|
||||
// Current
|
||||
let rep = tab.history.remove_or(&form.target);
|
||||
let rep = mem::replace(&mut tab.current, rep);
|
||||
tab.history.insert(rep.url.clone(), rep);
|
||||
|
||||
// Parent
|
||||
if let Some(parent) = form.target.parent() {
|
||||
tab.parent = Some(tab.history.remove_or(parent));
|
||||
}
|
||||
|
||||
err!(Pubsub::pub_after_cd(tab.id, tab.cwd()));
|
||||
act!(mgr:displace, cx)?;
|
||||
act!(mgr:hidden, cx).ok();
|
||||
act!(mgr:sort, cx).ok();
|
||||
act!(mgr:hover, cx)?;
|
||||
act!(mgr:refresh, cx)?;
|
||||
act!(app:title, cx).ok();
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
||||
impl Cd {
|
||||
fn cd_interactive(cx: &mut Ctx) -> Result<Data> {
|
||||
let input = input!(cx, YAZI.input.cd(cx.cwd().as_url()))?;
|
||||
|
||||
tokio::spawn(async move {
|
||||
let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50));
|
||||
pin!(rx);
|
||||
|
||||
while let Some(result) = rx.next().await {
|
||||
match result {
|
||||
InputEvent::Submit(s) => {
|
||||
let Ok(url) = UrlBuf::try_from(s).map(expand_url) else { return };
|
||||
let Ok(url) = engine::absolute(&url).await else { return };
|
||||
let url = clean_url(url);
|
||||
|
||||
let Ok(file) = engine::file(&url).await else { return };
|
||||
if file.is_dir() {
|
||||
return MgrProxy::cd(&url, CdSource::Cd);
|
||||
}
|
||||
|
||||
if let Some((p, k)) = url.pair2() {
|
||||
FilesOp::Upserting(p.into(), [(k.into(), file)].into()).emit();
|
||||
}
|
||||
MgrProxy::reveal(url);
|
||||
}
|
||||
InputEvent::Trigger(before, ticket) => {
|
||||
CmpProxy::trigger(before, ticket);
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
});
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::act;
|
||||
use yazi_parser::{mgr::CloseForm, spark::SparkKind};
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct Close;
|
||||
|
||||
impl Actor for Close {
|
||||
type Form = CloseForm;
|
||||
|
||||
const NAME: &str = "close";
|
||||
|
||||
fn act(cx: &mut Ctx, Self::Form { opt }: Self::Form) -> Result<Data> {
|
||||
if cx.tabs().len() > 1 {
|
||||
act!(mgr:tab_close, cx, cx.tabs().cursor)
|
||||
} else {
|
||||
act!(mgr:quit, cx, opt)
|
||||
}
|
||||
}
|
||||
|
||||
fn hook(cx: &Ctx, _form: &Self::Form) -> Option<SparkKind> {
|
||||
cx.source().is_key().then_some(SparkKind::KeyClose)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue