├── .cargo └── config.toml ├── .envrc ├── .github ├── DISCUSSION_TEMPLATE │ └── 1-q-a.yml ├── ISSUE_TEMPLATE │ ├── bug.yml │ ├── config.yml │ └── feature.yml ├── pull_request_template.md └── workflows │ ├── cachix.yml │ ├── check.yml │ ├── draft.yml │ ├── lock.yml │ ├── no-response.yml │ ├── publish.yml │ ├── test.yml │ └── validate-form.yml ├── .gitignore ├── .styluaignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── LICENSE-ICONS ├── README.md ├── assets ├── logo.png └── yazi.desktop ├── cspell.json ├── flake.lock ├── flake.nix ├── nix ├── shell.nix ├── yazi-unwrapped.nix └── yazi.nix ├── rustfmt.toml ├── scripts ├── build.sh ├── bump.sh ├── icons │ └── generate.lua ├── publish.sh └── validate-form │ ├── main.js │ ├── package-lock.json │ └── package.json ├── snap └── snapcraft.yaml ├── stylua.toml ├── yazi-adapter ├── Cargo.toml └── src │ ├── adapter.rs │ ├── brand.rs │ ├── dimension.rs │ ├── drivers │ ├── chafa.rs │ ├── iip.rs │ ├── kgp.rs │ ├── kgp_old.rs │ ├── mod.rs │ ├── sixel.rs │ └── ueberzug.rs │ ├── emulator.rs │ ├── image.rs │ ├── info.rs │ ├── lib.rs │ ├── mux.rs │ └── unknown.rs ├── yazi-binding ├── Cargo.toml └── src │ ├── error.rs │ ├── id.rs │ ├── lib.rs │ ├── macros.rs │ ├── stage.rs │ ├── url.rs │ └── urn.rs ├── yazi-boot ├── Cargo.toml ├── build.rs └── src │ ├── actions │ ├── actions.rs │ ├── clear_cache.rs │ ├── debug.rs │ ├── mod.rs │ ├── rustc.rs │ ├── triple.rs │ └── version.rs │ ├── args.rs │ ├── boot.rs │ └── lib.rs ├── yazi-cli ├── Cargo.toml ├── build.rs └── src │ ├── args.rs │ ├── main.rs │ └── package │ ├── add.rs │ ├── delete.rs │ ├── dependency.rs │ ├── deploy.rs │ ├── git.rs │ ├── hash.rs │ ├── install.rs │ ├── mod.rs │ ├── package.rs │ └── upgrade.rs ├── yazi-codegen ├── Cargo.toml └── src │ └── lib.rs ├── yazi-config ├── Cargo.toml ├── preset │ ├── README.md │ ├── keymap-default.toml │ ├── theme-dark.toml │ ├── theme-light.toml │ └── yazi-default.toml └── src │ ├── keymap │ ├── chord.rs │ ├── cow.rs │ ├── deserializers.rs │ ├── key.rs │ ├── keymap.rs │ ├── mod.rs │ └── rules.rs │ ├── layout.rs │ ├── lib.rs │ ├── mgr │ ├── mgr.rs │ ├── mod.rs │ ├── mouse.rs │ └── ratio.rs │ ├── open │ ├── mod.rs │ ├── open.rs │ └── rule.rs │ ├── opener │ ├── mod.rs │ ├── opener.rs │ └── rule.rs │ ├── pattern.rs │ ├── platform.rs │ ├── plugin │ ├── fetcher.rs │ ├── mod.rs │ ├── plugin.rs │ ├── preloader.rs │ ├── previewer.rs │ └── spotter.rs │ ├── popup │ ├── confirm.rs │ ├── input.rs │ ├── mod.rs │ ├── offset.rs │ ├── options.rs │ ├── origin.rs │ ├── pick.rs │ └── position.rs │ ├── preset.rs │ ├── preview │ ├── mod.rs │ ├── preview.rs │ └── wrap.rs │ ├── priority.rs │ ├── tasks │ ├── mod.rs │ └── tasks.rs │ ├── theme │ ├── filetype.rs │ ├── flavor.rs │ ├── icon.rs │ ├── is.rs │ ├── mod.rs │ └── theme.rs │ ├── which │ ├── mod.rs │ ├── sorting.rs │ └── which.rs │ └── yazi.rs ├── yazi-core ├── Cargo.toml └── src │ ├── cmp │ ├── cmp.rs │ ├── commands │ │ ├── arrow.rs │ │ ├── close.rs │ │ ├── mod.rs │ │ ├── show.rs │ │ └── trigger.rs │ └── mod.rs │ ├── confirm │ ├── commands │ │ ├── arrow.rs │ │ ├── close.rs │ │ ├── mod.rs │ │ └── show.rs │ ├── confirm.rs │ └── mod.rs │ ├── help │ ├── commands │ │ ├── arrow.rs │ │ ├── escape.rs │ │ ├── filter.rs │ │ └── mod.rs │ ├── help.rs │ └── mod.rs │ ├── input │ ├── commands │ │ ├── close.rs │ │ ├── escape.rs │ │ ├── mod.rs │ │ └── show.rs │ ├── input.rs │ └── mod.rs │ ├── lib.rs │ ├── mgr │ ├── commands │ │ ├── bulk_rename.rs │ │ ├── close.rs │ │ ├── create.rs │ │ ├── hardlink.rs │ │ ├── link.rs │ │ ├── mod.rs │ │ ├── open.rs │ │ ├── paste.rs │ │ ├── peek.rs │ │ ├── quit.rs │ │ ├── refresh.rs │ │ ├── remove.rs │ │ ├── rename.rs │ │ ├── seek.rs │ │ ├── spot.rs │ │ ├── suspend.rs │ │ ├── tab_close.rs │ │ ├── tab_create.rs │ │ ├── tab_swap.rs │ │ ├── tab_switch.rs │ │ ├── unyank.rs │ │ ├── update_files.rs │ │ ├── update_mimes.rs │ │ ├── update_paged.rs │ │ ├── update_tasks.rs │ │ ├── update_yanked.rs │ │ ├── watch.rs │ │ └── yank.rs │ ├── linked.rs │ ├── mgr.rs │ ├── mimetype.rs │ ├── mod.rs │ ├── tabs.rs │ ├── watcher.rs │ └── yanked.rs │ ├── notify │ ├── commands │ │ ├── mod.rs │ │ ├── push.rs │ │ └── tick.rs │ ├── message.rs │ ├── mod.rs │ └── notify.rs │ ├── pick │ ├── commands │ │ ├── arrow.rs │ │ ├── close.rs │ │ ├── mod.rs │ │ └── show.rs │ ├── mod.rs │ └── pick.rs │ ├── spot │ ├── commands │ │ ├── arrow.rs │ │ ├── close.rs │ │ ├── copy.rs │ │ ├── mod.rs │ │ └── swipe.rs │ ├── mod.rs │ └── spot.rs │ ├── tab │ ├── backstack.rs │ ├── commands │ │ ├── arrow.rs │ │ ├── back.rs │ │ ├── cd.rs │ │ ├── copy.rs │ │ ├── enter.rs │ │ ├── escape.rs │ │ ├── filter.rs │ │ ├── filter_do.rs │ │ ├── find.rs │ │ ├── find_arrow.rs │ │ ├── find_do.rs │ │ ├── follow.rs │ │ ├── forward.rs │ │ ├── hidden.rs │ │ ├── hover.rs │ │ ├── leave.rs │ │ ├── linemode.rs │ │ ├── mod.rs │ │ ├── reveal.rs │ │ ├── search.rs │ │ ├── shell.rs │ │ ├── sort.rs │ │ ├── toggle.rs │ │ ├── toggle_all.rs │ │ ├── update_peeked.rs │ │ ├── update_spotted.rs │ │ └── visual_mode.rs │ ├── finder.rs │ ├── folder.rs │ ├── history.rs │ ├── mod.rs │ ├── mode.rs │ ├── preference.rs │ ├── preview.rs │ ├── selected.rs │ └── tab.rs │ ├── tasks │ ├── commands │ │ ├── arrow.rs │ │ ├── cancel.rs │ │ ├── close.rs │ │ ├── inspect.rs │ │ ├── mod.rs │ │ ├── open_with.rs │ │ ├── process_exec.rs │ │ └── show.rs │ ├── file.rs │ ├── mod.rs │ ├── plugin.rs │ ├── preload.rs │ ├── process.rs │ ├── progress.rs │ └── tasks.rs │ └── which │ ├── commands │ ├── callback.rs │ ├── mod.rs │ └── show.rs │ ├── mod.rs │ ├── sorter.rs │ └── which.rs ├── yazi-dds ├── Cargo.toml ├── build.rs └── src │ ├── body │ ├── body.rs │ ├── bulk.rs │ ├── bye.rs │ ├── cd.rs │ ├── custom.rs │ ├── delete.rs │ ├── hey.rs │ ├── hi.rs │ ├── hover.rs │ ├── load.rs │ ├── mod.rs │ ├── mount.rs │ ├── move.rs │ ├── rename.rs │ ├── tab.rs │ ├── trash.rs │ └── yank.rs │ ├── client.rs │ ├── lib.rs │ ├── payload.rs │ ├── pubsub.rs │ ├── pump.rs │ ├── sendable.rs │ ├── server.rs │ ├── state.rs │ └── stream.rs ├── yazi-ffi ├── Cargo.toml └── src │ ├── cf_dict.rs │ ├── cf_string.rs │ ├── disk_arbitration.rs │ ├── io_kit.rs │ └── lib.rs ├── yazi-fm ├── Cargo.toml ├── build.rs └── src │ ├── app │ ├── app.rs │ ├── commands │ │ ├── accept_payload.rs │ │ ├── mod.rs │ │ ├── mouse.rs │ │ ├── notify.rs │ │ ├── plugin.rs │ │ ├── quit.rs │ │ ├── reflow.rs │ │ ├── render.rs │ │ ├── resize.rs │ │ ├── resume.rs │ │ ├── stop.rs │ │ ├── update_notify.rs │ │ └── update_progress.rs │ └── mod.rs │ ├── cmp │ ├── cmp.rs │ └── mod.rs │ ├── confirm │ ├── buttons.rs │ ├── confirm.rs │ ├── content.rs │ ├── list.rs │ └── mod.rs │ ├── context.rs │ ├── executor.rs │ ├── help │ ├── bindings.rs │ ├── help.rs │ └── mod.rs │ ├── input │ ├── input.rs │ └── mod.rs │ ├── lives │ ├── context.rs │ ├── file.rs │ ├── files.rs │ ├── filter.rs │ ├── finder.rs │ ├── folder.rs │ ├── iter.rs │ ├── lives.rs │ ├── mod.rs │ ├── mode.rs │ ├── preference.rs │ ├── preview.rs │ ├── ptr.rs │ ├── selected.rs │ ├── tab.rs │ ├── tabs.rs │ ├── tasks.rs │ └── yanked.rs │ ├── logs.rs │ ├── main.rs │ ├── mgr │ ├── mod.rs │ ├── modal.rs │ └── preview.rs │ ├── notify │ ├── mod.rs │ └── notify.rs │ ├── panic.rs │ ├── pick │ ├── mod.rs │ └── pick.rs │ ├── root.rs │ ├── router.rs │ ├── signals.rs │ ├── spot │ ├── mod.rs │ └── spot.rs │ ├── tasks │ ├── mod.rs │ ├── progress.rs │ └── tasks.rs │ ├── term.rs │ └── which │ ├── cand.rs │ ├── mod.rs │ └── which.rs ├── yazi-fs ├── Cargo.toml └── src │ ├── calculator.rs │ ├── cha │ ├── cha.rs │ ├── kind.rs │ └── mod.rs │ ├── cwd.rs │ ├── file.rs │ ├── files.rs │ ├── filter.rs │ ├── fns.rs │ ├── lib.rs │ ├── mounts │ ├── linux.rs │ ├── macos.rs │ ├── mod.rs │ ├── partition.rs │ └── partitions.rs │ ├── op.rs │ ├── path.rs │ ├── sorter.rs │ ├── sorting.rs │ ├── stage.rs │ ├── step.rs │ └── xdg.rs ├── yazi-macro ├── Cargo.toml └── src │ ├── asset.rs │ ├── event.rs │ ├── lib.rs │ ├── log.rs │ ├── module.rs │ ├── platform.rs │ └── stdio.rs ├── yazi-plugin ├── Cargo.toml ├── preset │ ├── compat.lua │ ├── components │ │ ├── current.lua │ │ ├── entity.lua │ │ ├── header.lua │ │ ├── linemode.lua │ │ ├── marker.lua │ │ ├── modal.lua │ │ ├── parent.lua │ │ ├── preview.lua │ │ ├── progress.lua │ │ ├── rail.lua │ │ ├── root.lua │ │ ├── status.lua │ │ ├── tab.lua │ │ └── tabs.lua │ ├── plugins │ │ ├── archive.lua │ │ ├── code.lua │ │ ├── dds.lua │ │ ├── empty.lua │ │ ├── extract.lua │ │ ├── file.lua │ │ ├── folder.lua │ │ ├── font.lua │ │ ├── fzf.lua │ │ ├── image.lua │ │ ├── json.lua │ │ ├── magick.lua │ │ ├── mime.lua │ │ ├── noop.lua │ │ ├── pdf.lua │ │ ├── session.lua │ │ ├── svg.lua │ │ ├── video.lua │ │ └── zoxide.lua │ ├── setup.lua │ └── ya.lua └── src │ ├── bindings │ ├── calculator.rs │ ├── cha.rs │ ├── chan.rs │ ├── icon.rs │ ├── image.rs │ ├── input.rs │ ├── layer.rs │ ├── mod.rs │ ├── mouse.rs │ ├── permit.rs │ └── range.rs │ ├── clipboard.rs │ ├── composer.rs │ ├── config │ ├── mod.rs │ ├── plugin.rs │ ├── runtime.rs │ └── theme.rs │ ├── elements │ ├── align.rs │ ├── area.rs │ ├── bar.rs │ ├── border.rs │ ├── cell.rs │ ├── clear.rs │ ├── constraint.rs │ ├── edge.rs │ ├── elements.rs │ ├── gauge.rs │ ├── layout.rs │ ├── line.rs │ ├── list.rs │ ├── mod.rs │ ├── pad.rs │ ├── pos.rs │ ├── rect.rs │ ├── renderable.rs │ ├── row.rs │ ├── span.rs │ ├── style.rs │ ├── table.rs │ ├── text.rs │ ├── utils.rs │ └── wrap.rs │ ├── external │ ├── fd.rs │ ├── highlighter.rs │ ├── mod.rs │ ├── rg.rs │ └── rga.rs │ ├── file │ ├── file.rs │ └── mod.rs │ ├── fs │ ├── fs.rs │ ├── mod.rs │ └── op.rs │ ├── isolate │ ├── entry.rs │ ├── fetch.rs │ ├── isolate.rs │ ├── mod.rs │ ├── peek.rs │ ├── preload.rs │ ├── seek.rs │ └── spot.rs │ ├── lib.rs │ ├── loader │ ├── chunk.rs │ ├── loader.rs │ ├── mod.rs │ └── require.rs │ ├── lua.rs │ ├── macros.rs │ ├── process │ ├── child.rs │ ├── command.rs │ ├── mod.rs │ ├── output.rs │ ├── process.rs │ └── status.rs │ ├── pubsub │ ├── mod.rs │ └── pubsub.rs │ ├── runtime.rs │ └── utils │ ├── app.rs │ ├── cache.rs │ ├── call.rs │ ├── image.rs │ ├── json.rs │ ├── layer.rs │ ├── log.rs │ ├── mod.rs │ ├── preview.rs │ ├── process.rs │ ├── spot.rs │ ├── sync.rs │ ├── target.rs │ ├── text.rs │ ├── time.rs │ ├── user.rs │ └── utils.rs ├── yazi-proxy ├── Cargo.toml └── src │ ├── app.rs │ ├── cmp.rs │ ├── confirm.rs │ ├── input.rs │ ├── lib.rs │ ├── macros.rs │ ├── mgr.rs │ ├── options │ ├── cmp.rs │ ├── mod.rs │ ├── notify.rs │ ├── open.rs │ ├── plugin.rs │ ├── process.rs │ └── search.rs │ ├── pick.rs │ ├── semaphore.rs │ ├── tab.rs │ └── tasks.rs ├── yazi-scheduler ├── Cargo.toml └── src │ ├── file │ ├── file.rs │ ├── in.rs │ ├── mod.rs │ └── out.rs │ ├── in.rs │ ├── lib.rs │ ├── ongoing.rs │ ├── out.rs │ ├── plugin │ ├── in.rs │ ├── mod.rs │ ├── out.rs │ └── plugin.rs │ ├── prework │ ├── in.rs │ ├── mod.rs │ ├── out.rs │ └── prework.rs │ ├── process │ ├── in.rs │ ├── mod.rs │ ├── process.rs │ └── shell.rs │ ├── scheduler.rs │ └── task.rs ├── yazi-shared ├── Cargo.toml └── src │ ├── chars.rs │ ├── condition.rs │ ├── debounce.rs │ ├── either.rs │ ├── env.rs │ ├── errors │ ├── input.rs │ ├── mod.rs │ └── peek.rs │ ├── event │ ├── cmd.rs │ ├── cow.rs │ ├── data.rs │ ├── event.rs │ └── mod.rs │ ├── id.rs │ ├── layer.rs │ ├── lib.rs │ ├── natsort.rs │ ├── number.rs │ ├── os.rs │ ├── rand.rs │ ├── ro_cell.rs │ ├── shell │ ├── mod.rs │ ├── unix.rs │ └── windows.rs │ ├── sync_cell.rs │ ├── terminal.rs │ ├── theme │ ├── color.rs │ ├── icon.rs │ ├── mod.rs │ └── style.rs │ ├── throttle.rs │ ├── time.rs │ ├── translit │ ├── mod.rs │ ├── table.rs │ └── traits.rs │ ├── url │ ├── loc.rs │ ├── mod.rs │ ├── url.rs │ └── urn.rs │ └── utf8.rs ├── yazi-term ├── Cargo.toml └── src │ ├── cursor.rs │ ├── if.rs │ ├── lib.rs │ └── tty │ ├── handle.rs │ ├── mod.rs │ ├── tty.rs │ └── windows.rs └── yazi-widgets ├── Cargo.toml └── src ├── input ├── commands │ ├── backspace.rs │ ├── backward.rs │ ├── commands.rs │ ├── complete.rs │ ├── delete.rs │ ├── escape.rs │ ├── forward.rs │ ├── insert.rs │ ├── kill.rs │ ├── mod.rs │ ├── move.rs │ ├── paste.rs │ ├── redo.rs │ ├── replace.rs │ ├── type.rs │ ├── undo.rs │ ├── visual.rs │ └── yank.rs ├── input.rs ├── mod.rs ├── mode.rs ├── op.rs ├── snap.rs ├── snaps.rs └── widget.rs └── lib.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | MACOSX_DEPLOYMENT_TARGET = "10.11" 3 | JEMALLOC_SYS_WITH_LG_PAGE = "16" 4 | 5 | # environment variable for tikv-jemalloc-sys 6 | # 7 | # https://jemalloc.net/jemalloc.3.html#opt.narenas 8 | # narenas is the maximum number of arenas to use for automatic multiplexing 9 | # of threads and arenas. The default is four times the number of CPUs, 10 | # or one if there is a single CPU. 11 | # 12 | # Improve memory efficiency by reducing fragmentation and ensuring all threads allocate from the same pool 13 | JEMALLOC_SYS_WITH_MALLOC_CONF = "narenas:1" 14 | 15 | [target.aarch64-apple-darwin] 16 | rustflags = [ "-Ctarget-cpu=apple-m1" ] 17 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | use flake -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 📝 Documentation Improvement 4 | url: https://github.com/yazi-rs/yazi-rs.github.io 5 | about: If you'd like to help improve the documentation 6 | - name: 💬 GitHub Discussions 7 | url: https://github.com/sxyazi/yazi/discussions/new?category=1-q-a 8 | about: When you have questions that are not bug reports or feature requests 9 | - name: 🌐 Discord Server / Telegram Group 10 | url: https://github.com/sxyazi/yazi#discussion 11 | about: If you'd prefer more realtime conversation with the community 12 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Which issue does this PR resolve? 2 | 3 | 8 | 9 | Resolves # 10 | 11 | ## Rationale of this PR 12 | 13 | 18 | -------------------------------------------------------------------------------- /.github/workflows/cachix.yml: -------------------------------------------------------------------------------- 1 | name: Cachix 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | publish: 9 | name: Publish Flake 10 | strategy: 11 | matrix: 12 | os: [ubuntu-latest, macos-latest] 13 | runs-on: ${{ matrix.os }} 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Install Nix 18 | uses: cachix/install-nix-action@v31 19 | 20 | - name: Authenticate with Cachix 21 | uses: cachix/cachix-action@v16 22 | with: 23 | name: yazi 24 | authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" 25 | 26 | - name: Build Flake 27 | run: nix build -L 28 | -------------------------------------------------------------------------------- /.github/workflows/lock.yml: -------------------------------------------------------------------------------- 1 | name: Lock Threads 2 | 3 | on: 4 | schedule: 5 | - cron: "5 3 * * *" 6 | workflow_dispatch: 7 | 8 | concurrency: 9 | group: lock 10 | 11 | jobs: 12 | lock: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | issues: write 16 | pull-requests: write 17 | discussions: write 18 | steps: 19 | - uses: dessant/lock-threads@v5 20 | with: 21 | issue-inactive-days: "30" 22 | issue-comment: > 23 | I'm going to lock this issue because it has been closed for _30 days_. ⏳ 24 | 25 | This helps our maintainers find and focus on the active issues. 26 | If you have found a problem that seems similar to this, please file a new 27 | issue and complete the issue template so we can capture all the details 28 | necessary to investigate further. 29 | pr-inactive-days: "30" 30 | discussion-inactive-days: "30" 31 | process-only: "issues,prs,discussions" 32 | -------------------------------------------------------------------------------- /.github/workflows/no-response.yml: -------------------------------------------------------------------------------- 1 | name: No Response 2 | 3 | on: 4 | issue_comment: 5 | types: [created] 6 | schedule: 7 | - cron: "10 * * * *" 8 | 9 | jobs: 10 | no-response: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | issues: write 14 | steps: 15 | - uses: lee-dohm/no-response@v0.5.0 16 | with: 17 | token: ${{ github.token }} 18 | daysUntilClose: 7 19 | responseRequiredLabel: waiting on op 20 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | winget: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/download-artifact@v4 12 | with: 13 | merge-multiple: true 14 | 15 | - name: Publish to Winget 16 | uses: vedantmgoyal9/winget-releaser@main 17 | with: 18 | identifier: sxyazi.yazi 19 | installers-regex: 'yazi-(x86_64|aarch64)-pc-windows-msvc\.zip$' 20 | token: ${{ secrets.WINGET_TOKEN }} 21 | 22 | - name: Promote snap to stable 23 | env: 24 | SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }} 25 | run: | 26 | sudo snap install --classic snapcraft 27 | snapcraft promote yazi --from-channel latest/candidate --to-channel latest/stable --yes 28 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | env: 10 | SCCACHE_GHA_ENABLED: true 11 | RUSTC_WRAPPER: sccache 12 | CARGO_TERM_COLOR: always 13 | 14 | jobs: 15 | test: 16 | strategy: 17 | matrix: 18 | os: [ubuntu-latest, windows-latest, macos-latest] 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - name: Setup Rust toolchain 24 | run: rustup toolchain install stable --profile minimal 25 | 26 | - name: Setup sccache 27 | uses: mozilla-actions/sccache-action@v0.0.9 28 | 29 | - name: Build 30 | run: cargo build --verbose 31 | 32 | - name: Test 33 | run: cargo test --workspace --verbose 34 | -------------------------------------------------------------------------------- /.github/workflows/validate-form.yml: -------------------------------------------------------------------------------- 1 | name: Validate Form 2 | 3 | on: 4 | issues: 5 | types: [opened, edited] 6 | schedule: 7 | - cron: "20 * * * *" 8 | 9 | jobs: 10 | check-version: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | issues: write 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Setup Node.js 18 | uses: actions/setup-node@v4 19 | with: 20 | node-js: 20 21 | 22 | - name: Install Dependencies 23 | run: | 24 | cd scripts/validate-form 25 | npm ci 26 | 27 | - name: Validate Form 28 | uses: actions/github-script@v7 29 | with: 30 | script: | 31 | const script = require('./scripts/validate-form/main.js') 32 | await script({github, context, core}) 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | yazi-*/completions 3 | node_modules/ 4 | 5 | .DS_Store 6 | 7 | result 8 | result-* 9 | .direnv 10 | 11 | .idea/ 12 | .vscode/ 13 | 14 | *.snap 15 | -------------------------------------------------------------------------------- /.styluaignore: -------------------------------------------------------------------------------- 1 | # this file caused some issues with the build system 2 | yazi-plugin/preset/plugins/mime.lua 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 - sxyazi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE-ICONS: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 nvim-tree 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyazi/yazi/d5038eeed52a459fa43b953c3b42a65a1b0d922c/assets/logo.png -------------------------------------------------------------------------------- /assets/yazi.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Yazi 3 | Icon=yazi 4 | Comment=Blazing fast terminal file manager written in Rust, based on async I/O 5 | Terminal=true 6 | TryExec=yazi 7 | Exec=yazi %u 8 | Type=Application 9 | MimeType=inode/directory 10 | Categories=Utility;Core;System;FileTools;FileManager;ConsoleOnly 11 | Keywords=File;Manager;Explorer;Browser;Launcher 12 | -------------------------------------------------------------------------------- /nix/shell.nix: -------------------------------------------------------------------------------- 1 | { 2 | callPackage, 3 | rust-bin, 4 | nodePackages, 5 | }: 6 | let 7 | mainPkg = callPackage ./yazi.nix { }; 8 | in 9 | mainPkg.overrideAttrs (oa: { 10 | nativeBuildInputs = [ 11 | (rust-bin.stable.latest.default.override { 12 | extensions = [ 13 | "rust-src" 14 | "rustfmt" 15 | "rust-analyzer" 16 | "clippy" 17 | ]; 18 | }) 19 | 20 | nodePackages.cspell 21 | ] ++ (oa.nativeBuildInputs or [ ]); 22 | 23 | env.RUST_BACKTRACE = "1"; 24 | }) 25 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | export ARTIFACT_NAME="yazi-$1" 5 | export YAZI_GEN_COMPLETIONS=1 6 | 7 | # Build for the target 8 | git config --global --add safe.directory "*" 9 | cargo build --release --locked --target "$1" 10 | 11 | # Create the artifact 12 | mkdir -p "$ARTIFACT_NAME/completions" 13 | cp "target/$1/release/ya" "$ARTIFACT_NAME" 14 | cp "target/$1/release/yazi" "$ARTIFACT_NAME" 15 | cp yazi-cli/completions/* "$ARTIFACT_NAME/completions" 16 | cp yazi-boot/completions/* "$ARTIFACT_NAME/completions" 17 | cp README.md LICENSE "$ARTIFACT_NAME" 18 | 19 | # Zip the artifact 20 | if ! command -v zip &> /dev/null 21 | then 22 | sudo apt-get update && sudo apt-get install -yq zip 23 | fi 24 | zip -r "$ARTIFACT_NAME.zip" "$ARTIFACT_NAME" 25 | -------------------------------------------------------------------------------- /scripts/bump.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 5 | cd "$SCRIPT_DIR/.." 6 | 7 | echo "Bumping version: $1" 8 | 9 | TOML_FILES="$(git ls-files '*Cargo.toml')" 10 | perl -pi -e "s/^version .*= .*\$/version = \"$1\"/" -- $TOML_FILES 11 | perl -pi -e "s/^(yazi-[a-z]+)\\s*=\\s*{.*\$/\\1 = { path = \"..\/\\1\", version = \"$1\" }/" -- $TOML_FILES 12 | 13 | ESLINT_USE_FLAT_CONFIG=true eslint -c ~/.config/rules/eslint/eslint.config.cjs --fix -- $TOML_FILES 14 | -------------------------------------------------------------------------------- /scripts/publish.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cargo publish -p yazi-macro && sleep 30 4 | cargo publish -p yazi-codegen && sleep 30 5 | cargo publish -p yazi-shared && sleep 30 6 | cargo publish -p yazi-ffi && sleep 30 7 | cargo publish -p yazi-fs && sleep 30 8 | cargo publish -p yazi-term && sleep 30 9 | cargo publish -p yazi-config && sleep 30 10 | cargo publish -p yazi-proxy && sleep 30 11 | cargo publish -p yazi-adapter && sleep 30 12 | cargo publish -p yazi-boot && sleep 30 13 | cargo publish -p yazi-binding && sleep 30 14 | cargo publish -p yazi-dds && sleep 30 15 | cargo publish -p yazi-scheduler && sleep 30 16 | cargo publish -p yazi-plugin && sleep 30 17 | cargo publish -p yazi-widgets && sleep 30 18 | cargo publish -p yazi-core && sleep 30 19 | cargo publish -p yazi-fm && sleep 30 20 | cargo publish -p yazi-cli 21 | -------------------------------------------------------------------------------- /scripts/validate-form/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "validate-form", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "test": "echo \"Error: no test specified\" && exit 1" 6 | }, 7 | "private": true, 8 | "dependencies": { 9 | "@actions/core": "^1.10.1", 10 | "@actions/github": "^6.0.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /stylua.toml: -------------------------------------------------------------------------------- 1 | indent_width = 2 2 | call_parentheses = "NoSingleTable" 3 | collapse_simple_statement = "FunctionOnly" 4 | 5 | [sort_requires] 6 | enabled = true 7 | -------------------------------------------------------------------------------- /yazi-adapter/src/drivers/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(chafa iip kgp kgp_old sixel ueberzug); 2 | -------------------------------------------------------------------------------- /yazi-adapter/src/unknown.rs: -------------------------------------------------------------------------------- 1 | use crate::Adapter; 2 | 3 | #[derive(Debug, Clone, Copy)] 4 | pub struct Unknown { 5 | pub kgp: bool, 6 | pub sixel: bool, 7 | } 8 | 9 | impl Unknown { 10 | pub(super) const fn default() -> Self { Self { kgp: false, sixel: false } } 11 | 12 | pub(super) fn adapters(self) -> &'static [Adapter] { 13 | use Adapter as A; 14 | 15 | match (self.kgp, self.sixel) { 16 | (true, true) => &[A::Sixel, A::KgpOld], 17 | (true, false) => &[A::KgpOld], 18 | (false, true) => &[A::Sixel], 19 | (false, false) => &[], 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /yazi-binding/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yazi-binding" 3 | version = "25.5.31" 4 | edition = "2024" 5 | license = "MIT" 6 | authors = [ "sxyazi " ] 7 | description = "Yazi Lua bindings" 8 | homepage = "https://yazi-rs.github.io" 9 | repository = "https://github.com/sxyazi/yazi" 10 | 11 | [dependencies] 12 | yazi-fs = { path = "../yazi-fs", version = "25.5.31" } 13 | yazi-macro = { path = "../yazi-macro", version = "25.5.31" } 14 | yazi-shared = { path = "../yazi-shared", version = "25.5.31" } 15 | 16 | # External dependencies 17 | mlua = { workspace = true } 18 | paste = { workspace = true } 19 | serde_json = { workspace = true } 20 | -------------------------------------------------------------------------------- /yazi-binding/src/id.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use mlua::{ExternalError, ExternalResult, FromLua, Lua, UserData, Value}; 4 | 5 | #[derive(Clone, Copy)] 6 | pub struct Id(pub yazi_shared::Id); 7 | 8 | impl Deref for Id { 9 | type Target = yazi_shared::Id; 10 | 11 | #[inline] 12 | fn deref(&self) -> &Self::Target { &self.0 } 13 | } 14 | 15 | impl FromLua for Id { 16 | fn from_lua(value: Value, _: &Lua) -> mlua::Result { 17 | Ok(match value { 18 | Value::Integer(i) => Self(i.try_into().into_lua_err()?), 19 | Value::UserData(ud) => *ud.borrow::()?, 20 | _ => Err("expected integer or userdata".into_lua_err())?, 21 | }) 22 | } 23 | } 24 | 25 | impl UserData for Id { 26 | fn add_fields>(fields: &mut F) { 27 | fields.add_field_method_get("value", |_, me| Ok(me.0.get())); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /yazi-binding/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod macros; 2 | 3 | yazi_macro::mod_flat!(error id stage url urn); 4 | -------------------------------------------------------------------------------- /yazi-binding/src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! cached_field { 3 | ($fields:ident, $key:ident, $value:expr) => { 4 | $fields.add_field_function_get(stringify!($key), |lua, ud| { 5 | use mlua::{Error::UserDataDestructed, IntoLua, Lua, Result, Value, Value::UserData}; 6 | ud.borrow_mut_scoped::>(|me| match paste::paste! { &me.[] } { 7 | Some(v) if !v.is_userdata() => Ok(v.clone()), 8 | Some(v @ UserData(ud)) if !matches!(ud.borrow::<()>(), Err(UserDataDestructed)) => { 9 | Ok(v.clone()) 10 | } 11 | _ => { 12 | let v = ($value as fn(&Lua, &Self) -> Result<_>)(lua, me)?.into_lua(lua)?; 13 | paste::paste! { me.[] = Some(v.clone()) }; 14 | Ok(v) 15 | } 16 | })? 17 | }); 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /yazi-binding/src/stage.rs: -------------------------------------------------------------------------------- 1 | use mlua::{IntoLuaMulti, MetaMethod, UserData, UserDataMethods}; 2 | 3 | pub struct FolderStage(yazi_fs::FolderStage); 4 | 5 | impl FolderStage { 6 | pub fn new(inner: yazi_fs::FolderStage) -> Self { Self(inner) } 7 | } 8 | 9 | impl UserData for FolderStage { 10 | fn add_methods>(methods: &mut M) { 11 | methods.add_meta_method(MetaMethod::Call, |lua, me, ()| { 12 | use yazi_fs::FolderStage::*; 13 | 14 | match me.0 { 15 | Loading => false.into_lua_multi(lua), 16 | Loaded => true.into_lua_multi(lua), 17 | Failed(kind) => (true, crate::Error::IoKind(kind)).into_lua_multi(lua), 18 | } 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /yazi-binding/src/urn.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use mlua::{ExternalError, FromLua, Lua, UserData, Value}; 4 | 5 | pub struct Urn { 6 | inner: yazi_shared::url::UrnBuf, 7 | } 8 | 9 | impl Deref for Urn { 10 | type Target = yazi_shared::url::UrnBuf; 11 | 12 | fn deref(&self) -> &Self::Target { &self.inner } 13 | } 14 | 15 | impl From for yazi_shared::url::UrnBuf { 16 | fn from(value: Urn) -> Self { value.inner } 17 | } 18 | 19 | impl Urn { 20 | pub fn new(urn: yazi_shared::url::UrnBuf) -> Self { Self { inner: urn } } 21 | } 22 | 23 | impl FromLua for Urn { 24 | fn from_lua(value: Value, _: &Lua) -> mlua::Result { 25 | Ok(match value { 26 | Value::UserData(ud) => ud.take()?, 27 | _ => Err("Expected a Urn".into_lua_err())?, 28 | }) 29 | } 30 | } 31 | 32 | impl UserData for Urn {} 33 | -------------------------------------------------------------------------------- /yazi-boot/src/actions/actions.rs: -------------------------------------------------------------------------------- 1 | use std::process; 2 | 3 | pub struct Actions; 4 | 5 | impl Actions { 6 | pub(crate) fn act(args: &crate::Args) { 7 | if args.debug { 8 | println!("{}", Self::debug().unwrap()); 9 | process::exit(0); 10 | } 11 | 12 | if args.version { 13 | println!("Yazi {}", Self::version()); 14 | process::exit(0); 15 | } 16 | 17 | if args.clear_cache { 18 | Self::clear_cache(); 19 | process::exit(0); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /yazi-boot/src/actions/clear_cache.rs: -------------------------------------------------------------------------------- 1 | use yazi_config::YAZI; 2 | use yazi_fs::Xdg; 3 | 4 | use super::Actions; 5 | 6 | impl Actions { 7 | pub(super) fn clear_cache() { 8 | if YAZI.preview.cache_dir == Xdg::cache_dir() { 9 | println!("Clearing cache directory: \n{:?}", YAZI.preview.cache_dir); 10 | std::fs::remove_dir_all(&YAZI.preview.cache_dir).unwrap(); 11 | } else { 12 | println!( 13 | "You've changed the default cache directory, for your data's safety, please clear it manually: \n{:?}", 14 | YAZI.preview.cache_dir 15 | ); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /yazi-boot/src/actions/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!(actions clear_cache debug rustc triple version); 4 | -------------------------------------------------------------------------------- /yazi-boot/src/actions/rustc.rs: -------------------------------------------------------------------------------- 1 | use super::Actions; 2 | 3 | impl Actions { 4 | pub(super) fn rustc() -> String { 5 | format!( 6 | "{} ({} {})", 7 | env!("VERGEN_RUSTC_SEMVER"), 8 | &env!("VERGEN_RUSTC_COMMIT_HASH")[..8], 9 | env!("VERGEN_RUSTC_COMMIT_DATE") 10 | ) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /yazi-boot/src/actions/triple.rs: -------------------------------------------------------------------------------- 1 | use super::Actions; 2 | 3 | impl Actions { 4 | pub(super) fn triple() -> String { 5 | format!( 6 | "{} ({}-{})", 7 | env!("VERGEN_RUSTC_HOST_TRIPLE"), 8 | std::env::consts::OS, 9 | std::env::consts::ARCH 10 | ) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /yazi-boot/src/actions/version.rs: -------------------------------------------------------------------------------- 1 | use super::Actions; 2 | 3 | impl Actions { 4 | pub fn version() -> &'static str { 5 | concat!( 6 | env!("CARGO_PKG_VERSION"), 7 | " (", 8 | env!("VERGEN_GIT_SHA"), 9 | " ", 10 | env!("VERGEN_BUILD_DATE"), 11 | ")" 12 | ) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /yazi-boot/src/args.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use clap::{Parser, command}; 4 | use yazi_shared::Id; 5 | 6 | #[derive(Debug, Default, Parser)] 7 | #[command(name = "yazi")] 8 | pub struct Args { 9 | /// Set the current working entry 10 | #[arg(index = 1, num_args = 1..=9)] 11 | pub entries: Vec, 12 | 13 | /// Write the cwd on exit to this file 14 | #[arg(long)] 15 | pub cwd_file: Option, 16 | /// Write the selected files to this file on open fired 17 | #[arg(long)] 18 | pub chooser_file: Option, 19 | 20 | /// Clear the cache directory 21 | #[arg(long)] 22 | pub clear_cache: bool, 23 | 24 | /// Use the specified client ID, must be a globally unique number 25 | #[arg(long)] 26 | pub client_id: Option, 27 | /// Report the specified local events to stdout 28 | #[arg(long)] 29 | pub local_events: Option, 30 | /// Report the specified remote events to stdout 31 | #[arg(long)] 32 | pub remote_events: Option, 33 | 34 | /// Print debug information 35 | #[arg(long)] 36 | pub debug: bool, 37 | 38 | /// Print version 39 | #[arg(short = 'V', long)] 40 | pub version: bool, 41 | } 42 | -------------------------------------------------------------------------------- /yazi-boot/src/lib.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_pub!(actions); 2 | 3 | yazi_macro::mod_flat!(args boot); 4 | 5 | use clap::Parser; 6 | use yazi_shared::RoCell; 7 | 8 | pub static ARGS: RoCell = RoCell::new(); 9 | pub static BOOT: RoCell = RoCell::new(); 10 | 11 | pub fn init() { 12 | ARGS.with(<_>::parse); 13 | BOOT.init(<_>::from(&*ARGS)); 14 | 15 | actions::Actions::act(&ARGS); 16 | } 17 | 18 | pub fn init_default() { 19 | ARGS.with(<_>::default); 20 | BOOT.with(<_>::default); 21 | } 22 | -------------------------------------------------------------------------------- /yazi-cli/build.rs: -------------------------------------------------------------------------------- 1 | #[path = "src/args.rs"] 2 | mod args; 3 | 4 | use std::{env, error::Error}; 5 | 6 | use clap::CommandFactory; 7 | use clap_complete::{Shell, generate_to}; 8 | use vergen_gitcl::{BuildBuilder, Emitter, GitclBuilder}; 9 | 10 | fn main() -> Result<(), Box> { 11 | Emitter::default() 12 | .add_instructions(&BuildBuilder::default().build_date(true).build()?)? 13 | .add_instructions(&GitclBuilder::default().commit_date(true).sha(true).build()?)? 14 | .emit()?; 15 | 16 | if env::var_os("YAZI_GEN_COMPLETIONS").is_none() { 17 | return Ok(()); 18 | } 19 | 20 | let cmd = &mut args::Args::command(); 21 | let bin = "ya"; 22 | let out = "completions"; 23 | 24 | std::fs::create_dir_all(out)?; 25 | for sh in [Shell::Bash, Shell::Fish, Shell::Zsh, Shell::Elvish, Shell::PowerShell] { 26 | generate_to(sh, cmd, bin, out)?; 27 | } 28 | 29 | generate_to(clap_complete_nushell::Nushell, cmd, bin, out)?; 30 | generate_to(clap_complete_fig::Fig, cmd, bin, out)?; 31 | 32 | Ok(()) 33 | } 34 | -------------------------------------------------------------------------------- /yazi-cli/src/package/add.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use yazi_fs::must_exists; 3 | 4 | use super::{Dependency, Git}; 5 | 6 | impl Dependency { 7 | pub(super) async fn add(&mut self) -> Result<()> { 8 | self.header("Upgrading package `{name}`")?; 9 | 10 | let path = self.local(); 11 | if must_exists(&path).await { 12 | Git::pull(&path).await?; 13 | } else { 14 | Git::clone(&self.remote(), &path).await?; 15 | }; 16 | 17 | self.deploy().await?; 18 | self.rev = Git::revision(&path).await?; 19 | Ok(()) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /yazi-cli/src/package/install.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use yazi_fs::must_exists; 3 | 4 | use super::{Dependency, Git}; 5 | 6 | impl Dependency { 7 | pub(super) async fn install(&mut self) -> Result<()> { 8 | self.header("Fetching package `{name}`")?; 9 | 10 | let path = self.local(); 11 | if must_exists(&path).await { 12 | Git::fetch(&path).await?; 13 | } else { 14 | Git::clone(&self.remote(), &path).await?; 15 | }; 16 | 17 | if !self.rev.is_empty() { 18 | Git::checkout(&path, self.rev.trim_start_matches('=')).await?; 19 | } 20 | 21 | self.deploy().await?; 22 | self.rev = Git::revision(&path).await?; 23 | Ok(()) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /yazi-cli/src/package/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!(add delete dependency deploy git hash install package upgrade); 4 | 5 | use anyhow::Context; 6 | use yazi_fs::Xdg; 7 | 8 | pub(super) fn init() -> anyhow::Result<()> { 9 | let root = Xdg::state_dir().join("packages"); 10 | std::fs::create_dir_all(&root) 11 | .with_context(|| format!("failed to create packages directory: {root:?}"))?; 12 | 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /yazi-cli/src/package/upgrade.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use super::Dependency; 4 | 5 | impl Dependency { 6 | pub(super) async fn upgrade(&mut self) -> Result<()> { 7 | if self.rev.starts_with('=') { Ok(()) } else { self.add().await } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /yazi-codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yazi-codegen" 3 | version = "25.5.31" 4 | edition = "2024" 5 | license = "MIT" 6 | authors = [ "sxyazi " ] 7 | description = "Yazi code generator" 8 | homepage = "https://yazi-rs.github.io" 9 | repository = "https://github.com/sxyazi/yazi" 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | [dependencies] 15 | # External dependencies 16 | syn = { version = "2.0.101", features = [ "full" ] } 17 | quote = "1.0.40" 18 | -------------------------------------------------------------------------------- /yazi-config/src/keymap/cow.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use yazi_shared::event::CmdCow; 4 | 5 | use super::Chord; 6 | 7 | #[derive(Debug)] 8 | pub enum ChordCow { 9 | Owned(Chord), 10 | Borrowed(&'static Chord), 11 | } 12 | 13 | impl From for ChordCow { 14 | fn from(c: Chord) -> Self { Self::Owned(c) } 15 | } 16 | 17 | impl From<&'static Chord> for ChordCow { 18 | fn from(c: &'static Chord) -> Self { Self::Borrowed(c) } 19 | } 20 | 21 | impl Deref for ChordCow { 22 | type Target = Chord; 23 | 24 | fn deref(&self) -> &Self::Target { 25 | match self { 26 | Self::Owned(c) => c, 27 | Self::Borrowed(c) => c, 28 | } 29 | } 30 | } 31 | 32 | impl Default for ChordCow { 33 | fn default() -> Self { Self::Owned(Chord::default()) } 34 | } 35 | 36 | impl ChordCow { 37 | pub fn into_seq(self) -> Vec { 38 | match self { 39 | Self::Owned(c) => c.run.into_iter().rev().map(|c| c.into()).collect(), 40 | Self::Borrowed(c) => c.run.iter().rev().map(|c| c.into()).collect(), 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /yazi-config/src/keymap/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(chord cow deserializers key keymap rules); 2 | -------------------------------------------------------------------------------- /yazi-config/src/layout.rs: -------------------------------------------------------------------------------- 1 | use ratatui::layout::Rect; 2 | 3 | #[derive(Clone, Copy, Default, PartialEq, Eq)] 4 | pub struct Layout { 5 | pub current: Rect, 6 | pub preview: Rect, 7 | pub progress: Rect, 8 | } 9 | 10 | impl Layout { 11 | pub const fn default() -> Self { 12 | Self { current: Rect::ZERO, preview: Rect::ZERO, progress: Rect::ZERO } 13 | } 14 | 15 | pub const fn limit(&self) -> usize { self.current.height as _ } 16 | } 17 | -------------------------------------------------------------------------------- /yazi-config/src/mgr/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(mgr mouse ratio); 2 | -------------------------------------------------------------------------------- /yazi-config/src/mgr/ratio.rs: -------------------------------------------------------------------------------- 1 | use anyhow::bail; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)] 5 | #[serde(try_from = "Vec")] 6 | pub struct MgrRatio { 7 | pub parent: u16, 8 | pub current: u16, 9 | pub preview: u16, 10 | pub all: u16, 11 | } 12 | 13 | impl TryFrom> for MgrRatio { 14 | type Error = anyhow::Error; 15 | 16 | fn try_from(ratio: Vec) -> Result { 17 | if ratio.len() != 3 { 18 | bail!("invalid layout ratio: {:?}", ratio); 19 | } 20 | if ratio.iter().all(|&r| r == 0) { 21 | bail!("at least one layout ratio must be non-zero: {:?}", ratio); 22 | } 23 | 24 | Ok(Self { 25 | parent: ratio[0], 26 | current: ratio[1], 27 | preview: ratio[2], 28 | all: ratio[0] + ratio[1] + ratio[2], 29 | }) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /yazi-config/src/open/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(open rule); 2 | -------------------------------------------------------------------------------- /yazi-config/src/opener/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(opener rule); 2 | -------------------------------------------------------------------------------- /yazi-config/src/opener/rule.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Result, bail}; 2 | use serde::Deserialize; 3 | 4 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 5 | pub struct OpenerRule { 6 | pub run: String, 7 | #[serde(default)] 8 | pub block: bool, 9 | #[serde(default)] 10 | pub orphan: bool, 11 | #[serde(default)] 12 | pub desc: String, 13 | pub r#for: Option, 14 | #[serde(skip)] 15 | pub spread: bool, 16 | } 17 | 18 | impl OpenerRule { 19 | #[inline] 20 | pub fn desc(&self) -> String { 21 | if !self.desc.is_empty() { 22 | self.desc.clone() 23 | } else if let Some(first) = self.run.split_whitespace().next() { 24 | first.to_owned() 25 | } else { 26 | String::new() 27 | } 28 | } 29 | } 30 | 31 | impl OpenerRule { 32 | pub(super) fn reshape(mut self) -> Result { 33 | if self.run.is_empty() { 34 | bail!("[open].rules.*.run cannot be empty."); 35 | } 36 | 37 | #[cfg(unix)] 38 | { 39 | self.spread = self.run.contains("$@") || self.run.contains("$*"); 40 | } 41 | #[cfg(windows)] 42 | { 43 | self.spread = self.run.contains("%*"); 44 | } 45 | 46 | Ok(self) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /yazi-config/src/platform.rs: -------------------------------------------------------------------------------- 1 | #[inline] 2 | pub(crate) fn check_for(r#for: Option<&str>) -> bool { 3 | match r#for.as_ref().map(|s| s.as_ref()) { 4 | Some("unix") if cfg!(unix) => true, 5 | Some(os) if os == std::env::consts::OS => true, 6 | Some(_) => false, 7 | None => true, 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /yazi-config/src/plugin/fetcher.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use serde::Deserialize; 4 | use yazi_shared::{MIME_DIR, event::Cmd}; 5 | 6 | use crate::{Pattern, Priority}; 7 | 8 | #[derive(Debug, Deserialize)] 9 | pub struct Fetcher { 10 | #[serde(skip)] 11 | pub idx: u8, 12 | 13 | pub id: String, 14 | pub name: Option, 15 | pub mime: Option, 16 | pub run: Cmd, 17 | #[serde(default)] 18 | pub prio: Priority, 19 | } 20 | 21 | impl Fetcher { 22 | #[inline] 23 | pub fn matches(&self, path: &Path, mime: &str) -> bool { 24 | self.mime.as_ref().is_some_and(|p| p.match_mime(mime)) 25 | || self.name.as_ref().is_some_and(|p| p.match_path(path, mime == MIME_DIR)) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /yazi-config/src/plugin/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(fetcher plugin preloader previewer spotter); 2 | 3 | pub const MAX_PREWORKERS: u8 = 32; 4 | -------------------------------------------------------------------------------- /yazi-config/src/plugin/preloader.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use serde::Deserialize; 4 | use yazi_shared::{MIME_DIR, event::Cmd}; 5 | 6 | use crate::{Pattern, Priority}; 7 | 8 | #[derive(Debug, Deserialize)] 9 | pub struct Preloader { 10 | #[serde(skip)] 11 | pub idx: u8, 12 | 13 | pub name: Option, 14 | pub mime: Option, 15 | pub run: Cmd, 16 | #[serde(default)] 17 | pub next: bool, 18 | #[serde(default)] 19 | pub prio: Priority, 20 | } 21 | 22 | impl Preloader { 23 | #[inline] 24 | pub fn matches(&self, path: &Path, mime: &str) -> bool { 25 | self.mime.as_ref().is_some_and(|p| p.match_mime(mime)) 26 | || self.name.as_ref().is_some_and(|p| p.match_path(path, mime == MIME_DIR)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /yazi-config/src/plugin/previewer.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use serde::Deserialize; 4 | use yazi_shared::{MIME_DIR, event::Cmd}; 5 | 6 | use crate::Pattern; 7 | 8 | #[derive(Debug, Deserialize)] 9 | pub struct Previewer { 10 | pub name: Option, 11 | pub mime: Option, 12 | pub run: Cmd, 13 | } 14 | 15 | impl Previewer { 16 | #[inline] 17 | pub fn matches(&self, path: &Path, mime: &str) -> bool { 18 | self.mime.as_ref().is_some_and(|p| p.match_mime(mime)) 19 | || self.name.as_ref().is_some_and(|p| p.match_path(path, mime == MIME_DIR)) 20 | } 21 | 22 | #[inline] 23 | pub fn any_file(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_file()) } 24 | 25 | #[inline] 26 | pub fn any_dir(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_dir()) } 27 | } 28 | -------------------------------------------------------------------------------- /yazi-config/src/plugin/spotter.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use serde::Deserialize; 4 | use yazi_shared::{MIME_DIR, event::Cmd}; 5 | 6 | use crate::Pattern; 7 | 8 | #[derive(Debug, Deserialize)] 9 | pub struct Spotter { 10 | pub name: Option, 11 | pub mime: Option, 12 | pub run: Cmd, 13 | } 14 | 15 | impl Spotter { 16 | #[inline] 17 | pub fn matches(&self, path: &Path, mime: &str) -> bool { 18 | self.mime.as_ref().is_some_and(|p| p.match_mime(mime)) 19 | || self.name.as_ref().is_some_and(|p| p.match_path(path, mime == MIME_DIR)) 20 | } 21 | 22 | #[inline] 23 | pub fn any_file(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_file()) } 24 | 25 | #[inline] 26 | pub fn any_dir(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_dir()) } 27 | } 28 | -------------------------------------------------------------------------------- /yazi-config/src/popup/confirm.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use yazi_codegen::DeserializeOver2; 3 | 4 | use super::{Offset, Origin}; 5 | 6 | #[derive(Deserialize, DeserializeOver2)] 7 | pub struct Confirm { 8 | // trash 9 | pub trash_title: String, 10 | pub trash_origin: Origin, 11 | pub trash_offset: Offset, 12 | 13 | // delete 14 | pub delete_title: String, 15 | pub delete_origin: Origin, 16 | pub delete_offset: Offset, 17 | 18 | // overwrite 19 | pub overwrite_title: String, 20 | pub overwrite_content: String, 21 | pub overwrite_origin: Origin, 22 | pub overwrite_offset: Offset, 23 | 24 | // quit 25 | pub quit_title: String, 26 | pub quit_content: String, 27 | pub quit_origin: Origin, 28 | pub quit_offset: Offset, 29 | } 30 | 31 | impl Confirm { 32 | pub const fn border(&self) -> u16 { 2 } 33 | } 34 | -------------------------------------------------------------------------------- /yazi-config/src/popup/input.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use yazi_codegen::DeserializeOver2; 3 | 4 | use super::{Offset, Origin}; 5 | 6 | #[derive(Deserialize, DeserializeOver2)] 7 | pub struct Input { 8 | pub cursor_blink: bool, 9 | 10 | // cd 11 | pub cd_title: String, 12 | pub cd_origin: Origin, 13 | pub cd_offset: Offset, 14 | 15 | // create 16 | pub create_title: [String; 2], 17 | pub create_origin: Origin, 18 | pub create_offset: Offset, 19 | 20 | // rename 21 | pub rename_title: String, 22 | pub rename_origin: Origin, 23 | pub rename_offset: Offset, 24 | 25 | // filter 26 | pub filter_title: String, 27 | pub filter_origin: Origin, 28 | pub filter_offset: Offset, 29 | 30 | // find 31 | pub find_title: [String; 2], 32 | pub find_origin: Origin, 33 | pub find_offset: Offset, 34 | 35 | // search 36 | pub search_title: String, 37 | pub search_origin: Origin, 38 | pub search_offset: Offset, 39 | 40 | // shell 41 | pub shell_title: [String; 2], 42 | pub shell_origin: Origin, 43 | pub shell_offset: Offset, 44 | } 45 | 46 | impl Input { 47 | pub const fn border(&self) -> u16 { 2 } 48 | } 49 | -------------------------------------------------------------------------------- /yazi-config/src/popup/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(confirm input offset options origin pick position); 2 | -------------------------------------------------------------------------------- /yazi-config/src/popup/offset.rs: -------------------------------------------------------------------------------- 1 | use anyhow::bail; 2 | use serde::Deserialize; 3 | 4 | #[derive(Clone, Copy, Debug, Default, Deserialize)] 5 | #[serde(try_from = "Vec")] 6 | pub struct Offset { 7 | pub x: i16, 8 | pub y: i16, 9 | pub width: u16, 10 | pub height: u16, 11 | } 12 | 13 | impl TryFrom> for Offset { 14 | type Error = anyhow::Error; 15 | 16 | fn try_from(values: Vec) -> Result { 17 | if values.len() != 4 { 18 | bail!("offset must have 4 values: {:?}", values); 19 | } 20 | if values[2] < 0 || values[3] < 0 { 21 | bail!("offset width and height must be positive: {:?}", values); 22 | } 23 | if values[3] < 3 { 24 | bail!("offset height must be at least 3: {:?}", values); 25 | } 26 | 27 | Ok(Self { 28 | x: values[0], 29 | y: values[1], 30 | width: values[2] as u16, 31 | height: values[3] as u16, 32 | }) 33 | } 34 | } 35 | 36 | impl Offset { 37 | #[inline] 38 | pub fn line() -> Self { Self { x: 0, y: 0, width: u16::MAX, height: 1 } } 39 | } 40 | -------------------------------------------------------------------------------- /yazi-config/src/popup/origin.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Display, str::FromStr}; 2 | 3 | use serde::Deserialize; 4 | 5 | #[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)] 6 | #[serde(rename_all = "kebab-case")] 7 | pub enum Origin { 8 | #[default] 9 | TopLeft, 10 | TopCenter, 11 | TopRight, 12 | 13 | BottomLeft, 14 | BottomCenter, 15 | BottomRight, 16 | 17 | Center, 18 | Hovered, 19 | } 20 | 21 | impl Display for Origin { 22 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 23 | f.write_str(match self { 24 | Self::TopLeft => "top-left", 25 | Self::TopCenter => "top-center", 26 | Self::TopRight => "top-right", 27 | 28 | Self::BottomLeft => "bottom-left", 29 | Self::BottomCenter => "bottom-center", 30 | Self::BottomRight => "bottom-right", 31 | 32 | Self::Center => "center", 33 | Self::Hovered => "hovered", 34 | }) 35 | } 36 | } 37 | 38 | impl FromStr for Origin { 39 | type Err = serde::de::value::Error; 40 | 41 | fn from_str(s: &str) -> Result { 42 | Self::deserialize(serde::de::value::StrDeserializer::new(s)) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /yazi-config/src/popup/pick.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use yazi_codegen::DeserializeOver2; 3 | 4 | use super::{Offset, Origin}; 5 | 6 | #[derive(Deserialize, DeserializeOver2)] 7 | pub struct Pick { 8 | // open 9 | pub open_title: String, 10 | pub open_origin: Origin, 11 | pub open_offset: Offset, 12 | } 13 | 14 | impl Pick { 15 | pub const fn border(&self) -> u16 { 2 } 16 | } 17 | -------------------------------------------------------------------------------- /yazi-config/src/preset.rs: -------------------------------------------------------------------------------- 1 | use crate::{Yazi, keymap::Keymap, theme::Theme}; 2 | 3 | pub(crate) struct Preset; 4 | 5 | impl Preset { 6 | #[inline] 7 | pub(super) fn yazi() -> Result { 8 | toml::from_str(&yazi_macro::config_preset!("yazi")) 9 | } 10 | 11 | #[inline] 12 | pub(super) fn keymap() -> Result { 13 | toml::from_str(&yazi_macro::config_preset!("keymap")) 14 | } 15 | 16 | #[inline] 17 | pub(super) fn theme(light: bool) -> Result { 18 | toml::from_str(&if light { 19 | yazi_macro::theme_preset!("light") 20 | } else { 21 | yazi_macro::theme_preset!("dark") 22 | }) 23 | } 24 | 25 | #[inline] 26 | pub(crate) fn mix(a: A, b: B, c: C) -> impl Iterator 27 | where 28 | A: IntoIterator, 29 | B: IntoIterator, 30 | C: IntoIterator, 31 | { 32 | a.into_iter().chain(b).chain(c) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /yazi-config/src/preview/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(preview wrap); 2 | -------------------------------------------------------------------------------- /yazi-config/src/preview/wrap.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)] 4 | #[serde(rename_all = "kebab-case")] 5 | pub enum PreviewWrap { 6 | No, 7 | Yes, 8 | } 9 | -------------------------------------------------------------------------------- /yazi-config/src/priority.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | 3 | #[derive(Default, Clone, Copy, Debug, Deserialize)] 4 | #[serde(rename_all = "kebab-case")] 5 | pub enum Priority { 6 | Low = 0, 7 | #[default] 8 | Normal = 1, 9 | High = 2, 10 | } 11 | -------------------------------------------------------------------------------- /yazi-config/src/tasks/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(tasks); 2 | -------------------------------------------------------------------------------- /yazi-config/src/tasks/tasks.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Result, bail}; 2 | use serde::Deserialize; 3 | use yazi_codegen::DeserializeOver2; 4 | 5 | #[derive(Debug, Deserialize, DeserializeOver2)] 6 | pub struct Tasks { 7 | pub micro_workers: u8, 8 | pub macro_workers: u8, 9 | pub bizarre_retry: u8, 10 | 11 | pub image_alloc: u32, 12 | pub image_bound: [u16; 2], 13 | 14 | pub suppress_preload: bool, 15 | } 16 | 17 | impl Tasks { 18 | pub(crate) fn reshape(self) -> Result { 19 | if self.micro_workers < 1 { 20 | bail!("[tasks].micro_workers must be at least 1."); 21 | } else if self.macro_workers < 1 { 22 | bail!("[tasks].macro_workers must be at least 1."); 23 | } else if self.bizarre_retry < 1 { 24 | bail!("[tasks].bizarre_retry` must be at least 1."); 25 | } 26 | Ok(self) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /yazi-config/src/theme/filetype.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use serde::Deserialize; 4 | use yazi_codegen::DeserializeOver2; 5 | use yazi_fs::File; 6 | use yazi_shared::theme::Style; 7 | 8 | use super::Is; 9 | use crate::Pattern; 10 | 11 | #[derive(Deserialize, DeserializeOver2)] 12 | pub struct Filetype { 13 | rules: Vec, 14 | } 15 | 16 | impl Deref for Filetype { 17 | type Target = Vec; 18 | 19 | fn deref(&self) -> &Self::Target { &self.rules } 20 | } 21 | 22 | #[derive(Deserialize)] 23 | pub struct FiletypeRule { 24 | #[serde(default)] 25 | is: Is, 26 | name: Option, 27 | mime: Option, 28 | #[serde(flatten)] 29 | pub style: Style, 30 | } 31 | 32 | impl FiletypeRule { 33 | pub fn matches(&self, file: &File, mime: &str) -> bool { 34 | if !self.is.check(&file.cha) { 35 | return false; 36 | } 37 | 38 | self.mime.as_ref().is_some_and(|p| p.match_mime(mime)) 39 | || self.name.as_ref().is_some_and(|n| n.match_path(&file.url, file.is_dir())) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /yazi-config/src/theme/is.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use yazi_fs::cha::Cha; 3 | 4 | #[derive(Default, Deserialize)] 5 | #[serde(rename_all = "kebab-case")] 6 | pub enum Is { 7 | #[default] 8 | None, 9 | Hidden, 10 | Link, 11 | Orphan, 12 | Dummy, 13 | Block, 14 | Char, 15 | Fifo, 16 | Sock, 17 | Exec, 18 | Sticky, 19 | } 20 | 21 | impl Is { 22 | #[inline] 23 | pub fn check(&self, cha: &Cha) -> bool { 24 | match self { 25 | Self::None => true, 26 | Self::Hidden => cha.is_hidden(), 27 | Self::Link => cha.is_link(), 28 | Self::Orphan => cha.is_orphan(), 29 | Self::Dummy => cha.is_dummy(), 30 | Self::Block => cha.is_block(), 31 | Self::Char => cha.is_char(), 32 | Self::Fifo => cha.is_fifo(), 33 | Self::Sock => cha.is_sock(), 34 | Self::Exec => cha.is_exec(), 35 | Self::Sticky => cha.is_sticky(), 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /yazi-config/src/theme/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(filetype flavor icon is theme); 2 | -------------------------------------------------------------------------------- /yazi-config/src/which/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(sorting which); 2 | -------------------------------------------------------------------------------- /yazi-config/src/which/sorting.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)] 4 | #[serde(rename_all = "kebab-case")] 5 | pub enum SortBy { 6 | #[default] 7 | None, 8 | Key, 9 | Desc, 10 | } 11 | -------------------------------------------------------------------------------- /yazi-config/src/which/which.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use yazi_codegen::DeserializeOver2; 3 | 4 | use super::SortBy; 5 | 6 | #[derive(Debug, Deserialize, DeserializeOver2, Serialize)] 7 | pub struct Which { 8 | // Sorting 9 | pub sort_by: SortBy, 10 | pub sort_sensitive: bool, 11 | pub sort_reverse: bool, 12 | pub sort_translit: bool, 13 | } 14 | -------------------------------------------------------------------------------- /yazi-config/src/yazi.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use serde::Deserialize; 3 | use yazi_codegen::DeserializeOver1; 4 | 5 | use crate::{mgr, open, opener, plugin, popup, preview, tasks, which}; 6 | 7 | #[derive(Deserialize, DeserializeOver1)] 8 | pub struct Yazi { 9 | pub mgr: mgr::Mgr, 10 | pub preview: preview::Preview, 11 | pub opener: opener::Opener, 12 | pub open: open::Open, 13 | pub tasks: tasks::Tasks, 14 | pub plugin: plugin::Plugin, 15 | pub input: popup::Input, 16 | pub confirm: popup::Confirm, 17 | pub pick: popup::Pick, 18 | pub which: which::Which, 19 | } 20 | 21 | impl Yazi { 22 | pub(super) fn reshape(self) -> Result { 23 | Ok(Self { 24 | mgr: self.mgr.reshape()?, 25 | preview: self.preview.reshape()?, 26 | opener: self.opener.reshape()?, 27 | open: self.open.reshape()?, 28 | tasks: self.tasks.reshape()?, 29 | plugin: self.plugin.reshape()?, 30 | input: self.input, 31 | confirm: self.confirm, 32 | pick: self.pick, 33 | which: self.which, 34 | }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /yazi-core/src/cmp/cmp.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, path::PathBuf}; 2 | 3 | use yazi_proxy::options::CmpItem; 4 | use yazi_shared::Id; 5 | 6 | #[derive(Default)] 7 | pub struct Cmp { 8 | pub(super) caches: HashMap>, 9 | pub(super) cands: Vec, 10 | pub(super) offset: usize, 11 | pub cursor: usize, 12 | 13 | pub(super) ticket: Id, 14 | pub visible: bool, 15 | } 16 | 17 | impl Cmp { 18 | // --- Cands 19 | #[inline] 20 | pub fn window(&self) -> &[CmpItem] { 21 | let end = (self.offset + self.limit()).min(self.cands.len()); 22 | &self.cands[self.offset..end] 23 | } 24 | 25 | #[inline] 26 | pub fn limit(&self) -> usize { self.cands.len().min(10) } 27 | 28 | #[inline] 29 | pub fn selected(&self) -> Option<&CmpItem> { self.cands.get(self.cursor) } 30 | 31 | // --- Cursor 32 | #[inline] 33 | pub fn rel_cursor(&self) -> usize { self.cursor - self.offset } 34 | } 35 | -------------------------------------------------------------------------------- /yazi-core/src/cmp/commands/close.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | use yazi_macro::render; 4 | use yazi_proxy::InputProxy; 5 | use yazi_shared::event::CmdCow; 6 | 7 | use crate::cmp::Cmp; 8 | 9 | struct Opt { 10 | submit: bool, 11 | } 12 | 13 | impl From for Opt { 14 | fn from(c: CmdCow) -> Self { Self { submit: c.bool("submit") } } 15 | } 16 | impl From for Opt { 17 | fn from(submit: bool) -> Self { Self { submit } } 18 | } 19 | 20 | impl Cmp { 21 | #[yazi_codegen::command] 22 | pub fn close(&mut self, opt: Opt) { 23 | if let Some(s) = self.selected().filter(|_| opt.submit) { 24 | InputProxy::complete(s, self.ticket); 25 | } 26 | 27 | self.caches.clear(); 28 | render!(mem::replace(&mut self.visible, false)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /yazi-core/src/cmp/commands/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(arrow close show trigger); 2 | -------------------------------------------------------------------------------- /yazi-core/src/cmp/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_pub!(commands); 2 | 3 | yazi_macro::mod_flat!(cmp); 4 | -------------------------------------------------------------------------------- /yazi-core/src/confirm/commands/arrow.rs: -------------------------------------------------------------------------------- 1 | use yazi_fs::Step; 2 | use yazi_macro::render; 3 | use yazi_shared::event::CmdCow; 4 | 5 | use crate::{confirm::Confirm, mgr::Mgr}; 6 | 7 | struct Opt { 8 | step: Step, 9 | } 10 | 11 | impl From for Opt { 12 | fn from(c: CmdCow) -> Self { 13 | Self { step: c.first().and_then(|d| d.try_into().ok()).unwrap_or_default() } 14 | } 15 | } 16 | 17 | impl Confirm { 18 | #[yazi_codegen::command] 19 | pub fn arrow(&mut self, opt: Opt, mgr: &Mgr) { 20 | let area = mgr.area(self.position); 21 | let len = self.list.line_count(area.width); 22 | 23 | let old = self.offset; 24 | self.offset = opt.step.add(self.offset, len, area.height as _); 25 | 26 | render!(old != self.offset); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /yazi-core/src/confirm/commands/close.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::confirm::Confirm; 5 | 6 | struct Opt { 7 | submit: bool, 8 | } 9 | 10 | impl From for Opt { 11 | fn from(c: CmdCow) -> Self { Self { submit: c.bool("submit") } } 12 | } 13 | impl From for Opt { 14 | fn from(submit: bool) -> Self { Self { submit } } 15 | } 16 | 17 | impl Confirm { 18 | #[yazi_codegen::command] 19 | pub fn close(&mut self, opt: Opt) { 20 | if let Some(cb) = self.callback.take() { 21 | _ = cb.send(opt.submit); 22 | } 23 | 24 | self.visible = false; 25 | render!(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /yazi-core/src/confirm/commands/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(arrow close show); 2 | -------------------------------------------------------------------------------- /yazi-core/src/confirm/commands/show.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::oneshot; 2 | use yazi_config::popup::ConfirmCfg; 3 | use yazi_macro::render; 4 | use yazi_shared::event::CmdCow; 5 | 6 | use crate::confirm::Confirm; 7 | 8 | pub struct Opt { 9 | cfg: ConfirmCfg, 10 | tx: oneshot::Sender, 11 | } 12 | 13 | impl TryFrom for Opt { 14 | type Error = (); 15 | 16 | fn try_from(mut c: CmdCow) -> Result { 17 | Ok(Self { cfg: c.take_any("cfg").ok_or(())?, tx: c.take_any("tx").ok_or(())? }) 18 | } 19 | } 20 | 21 | impl Confirm { 22 | pub fn show(&mut self, opt: impl TryInto) { 23 | let Ok(opt) = opt.try_into() else { 24 | return; 25 | }; 26 | 27 | self.close(false); 28 | self.title = opt.cfg.title; 29 | self.content = opt.cfg.content; 30 | self.list = opt.cfg.list; 31 | 32 | self.position = opt.cfg.position; 33 | self.offset = 0; 34 | 35 | self.callback = Some(opt.tx); 36 | self.visible = true; 37 | render!(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /yazi-core/src/confirm/confirm.rs: -------------------------------------------------------------------------------- 1 | use ratatui::{text::Line, widgets::Paragraph}; 2 | use tokio::sync::oneshot::Sender; 3 | use yazi_config::popup::Position; 4 | 5 | #[derive(Default)] 6 | pub struct Confirm { 7 | pub title: Line<'static>, 8 | pub content: Paragraph<'static>, 9 | pub list: Paragraph<'static>, 10 | 11 | pub position: Position, 12 | pub offset: usize, 13 | 14 | pub(super) callback: Option>, 15 | pub visible: bool, 16 | } 17 | -------------------------------------------------------------------------------- /yazi-core/src/confirm/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_pub!(commands); 2 | 3 | yazi_macro::mod_flat!(confirm); 4 | -------------------------------------------------------------------------------- /yazi-core/src/help/commands/escape.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::help::Help; 5 | 6 | impl Help { 7 | pub fn escape(&mut self, _: CmdCow) { 8 | if self.keyword().is_none() { 9 | return self.toggle(self.layer); 10 | } 11 | 12 | self.keyword = String::new(); 13 | self.in_filter = None; 14 | self.filter_apply(); 15 | render!(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /yazi-core/src/help/commands/filter.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::help::Help; 5 | 6 | impl Help { 7 | pub fn filter(&mut self, _: CmdCow) { 8 | self.in_filter = Some(Default::default()); 9 | self.filter_apply(); 10 | render!(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /yazi-core/src/help/commands/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(arrow escape filter); 2 | -------------------------------------------------------------------------------- /yazi-core/src/help/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_pub!(commands); 2 | 3 | yazi_macro::mod_flat!(help); 4 | 5 | pub const HELP_MARGIN: u16 = 1; 6 | -------------------------------------------------------------------------------- /yazi-core/src/input/commands/close.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_proxy::CmpProxy; 3 | use yazi_shared::{errors::InputError, event::CmdCow}; 4 | 5 | use crate::input::Input; 6 | 7 | struct Opt { 8 | submit: bool, 9 | } 10 | 11 | impl From for Opt { 12 | fn from(c: CmdCow) -> Self { Self { submit: c.bool("submit") } } 13 | } 14 | impl From for Opt { 15 | fn from(submit: bool) -> Self { Self { submit } } 16 | } 17 | 18 | impl Input { 19 | #[yazi_codegen::command] 20 | pub fn close(&mut self, opt: Opt) { 21 | self.visible = false; 22 | self.ticket.next(); 23 | 24 | if let Some(tx) = self.tx.take() { 25 | let value = self.snap().value.clone(); 26 | _ = tx.send(if opt.submit { Ok(value) } else { Err(InputError::Canceled(value)) }); 27 | } 28 | 29 | CmpProxy::close(); 30 | render!(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /yazi-core/src/input/commands/escape.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_proxy::CmpProxy; 3 | use yazi_shared::event::CmdCow; 4 | use yazi_widgets::input::InputOp; 5 | 6 | use crate::input::Input; 7 | 8 | struct Opt; 9 | 10 | impl From for Opt { 11 | fn from(_: CmdCow) -> Self { Self } 12 | } 13 | impl From<()> for Opt { 14 | fn from(_: ()) -> Self { Self } 15 | } 16 | 17 | impl Input { 18 | #[yazi_codegen::command] 19 | pub fn escape(&mut self, _: Opt) { 20 | use yazi_widgets::input::InputMode as M; 21 | 22 | let mode = self.snap().mode; 23 | match mode { 24 | M::Normal if self.snap_mut().op == InputOp::None => self.close(false), 25 | M::Insert => CmpProxy::close(), 26 | M::Normal | M::Replace => {} 27 | } 28 | 29 | self.inner.escape(()); 30 | render!(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /yazi-core/src/input/commands/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(close escape show); 2 | -------------------------------------------------------------------------------- /yazi-core/src/input/input.rs: -------------------------------------------------------------------------------- 1 | use std::{ops::{Deref, DerefMut}, rc::Rc}; 2 | 3 | use tokio::sync::mpsc::UnboundedSender; 4 | use yazi_config::popup::Position; 5 | use yazi_shared::{Ids, errors::InputError}; 6 | 7 | #[derive(Default)] 8 | pub struct Input { 9 | pub(super) inner: yazi_widgets::input::Input, 10 | 11 | pub visible: bool, 12 | pub title: String, 13 | pub position: Position, 14 | 15 | // Typing 16 | pub(super) tx: Option>>, 17 | pub(super) ticket: Rc, 18 | } 19 | 20 | impl Deref for Input { 21 | type Target = yazi_widgets::input::Input; 22 | 23 | fn deref(&self) -> &Self::Target { &self.inner } 24 | } 25 | 26 | impl DerefMut for Input { 27 | fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } 28 | } 29 | -------------------------------------------------------------------------------- /yazi-core/src/input/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_pub!(commands); 2 | 3 | yazi_macro::mod_flat!(input); 4 | -------------------------------------------------------------------------------- /yazi-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | clippy::if_same_then_else, 3 | clippy::len_without_is_empty, 4 | clippy::module_inception, 5 | clippy::option_map_unit_fn, 6 | clippy::unit_arg 7 | )] 8 | 9 | yazi_macro::mod_pub!(cmp confirm help input mgr notify pick spot tab tasks which); 10 | 11 | pub fn init() { 12 | mgr::WATCHED.with(<_>::default); 13 | mgr::LINKED.with(<_>::default); 14 | } 15 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/close.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::event::CmdCow; 2 | 3 | use crate::{mgr::{Mgr, commands::quit}, tasks::Tasks}; 4 | 5 | #[derive(Default)] 6 | struct Opt(quit::Opt); 7 | 8 | impl From for Opt { 9 | fn from(c: CmdCow) -> Self { Self(c.into()) } 10 | } 11 | 12 | impl Mgr { 13 | #[yazi_codegen::command] 14 | pub fn close(&mut self, opt: Opt, tasks: &Tasks) { 15 | if self.tabs.len() > 1 { 16 | return self.tabs.close(self.tabs.cursor); 17 | } 18 | self.quit(opt.0, tasks); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/hardlink.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::event::CmdCow; 2 | 3 | use crate::{mgr::Mgr, tasks::Tasks}; 4 | 5 | struct Opt { 6 | force: bool, 7 | follow: bool, 8 | } 9 | 10 | impl From for Opt { 11 | fn from(c: CmdCow) -> Self { Self { force: c.bool("force"), follow: c.bool("follow") } } 12 | } 13 | 14 | impl Mgr { 15 | #[yazi_codegen::command] 16 | pub fn hardlink(&mut self, opt: Opt, tasks: &Tasks) { 17 | if self.yanked.cut { 18 | return; 19 | } 20 | 21 | tasks.file_hardlink(&self.yanked, self.cwd(), opt.force, opt.follow); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/link.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::event::CmdCow; 2 | 3 | use crate::{mgr::Mgr, tasks::Tasks}; 4 | 5 | struct Opt { 6 | relative: bool, 7 | force: bool, 8 | } 9 | 10 | impl From for Opt { 11 | fn from(c: CmdCow) -> Self { Self { relative: c.bool("relative"), force: c.bool("force") } } 12 | } 13 | 14 | impl Mgr { 15 | #[yazi_codegen::command] 16 | pub fn link(&mut self, opt: Opt, tasks: &Tasks) { 17 | if self.yanked.cut { 18 | return; 19 | } 20 | 21 | tasks.file_link(&self.yanked, self.cwd(), opt.relative, opt.force); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!( 2 | bulk_rename 3 | close 4 | create 5 | hardlink 6 | link 7 | open 8 | paste 9 | peek 10 | quit 11 | refresh 12 | remove 13 | rename 14 | seek 15 | spot 16 | suspend 17 | tab_close 18 | tab_create 19 | tab_swap 20 | tab_switch 21 | unyank 22 | update_files 23 | update_mimes 24 | update_paged 25 | update_tasks 26 | update_yanked 27 | watch 28 | yank 29 | ); 30 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/paste.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::event::CmdCow; 2 | 3 | use crate::{mgr::Mgr, tasks::Tasks}; 4 | 5 | struct Opt { 6 | force: bool, 7 | follow: bool, 8 | } 9 | 10 | impl From for Opt { 11 | fn from(c: CmdCow) -> Self { Self { force: c.bool("force"), follow: c.bool("follow") } } 12 | } 13 | 14 | impl Mgr { 15 | #[yazi_codegen::command] 16 | pub fn paste(&mut self, opt: Opt, tasks: &Tasks) { 17 | let (src, dest) = (self.yanked.iter().collect::>(), self.cwd()); 18 | 19 | if self.yanked.cut { 20 | tasks.file_cut(&src, dest, opt.force); 21 | 22 | self.tabs.iter_mut().for_each(|t| _ = t.selected.remove_many(&src)); 23 | self.unyank(()); 24 | } else { 25 | tasks.file_copy(&src, dest, opt.force, opt.follow); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/refresh.rs: -------------------------------------------------------------------------------- 1 | use crossterm::{execute, terminal::SetTitle}; 2 | use yazi_config::YAZI; 3 | use yazi_fs::CWD; 4 | use yazi_shared::event::CmdCow; 5 | use yazi_term::tty::TTY; 6 | 7 | use crate::{mgr::Mgr, tasks::Tasks}; 8 | 9 | impl Mgr { 10 | pub fn refresh(&mut self, _: CmdCow, tasks: &Tasks) { 11 | if let (_, Some(s)) = (CWD.set(self.cwd()), YAZI.mgr.title()) { 12 | execute!(TTY.writer(), SetTitle(s)).ok(); 13 | } 14 | 15 | self.active_mut().apply_files_attrs(); 16 | 17 | if let Some(p) = self.parent() { 18 | self.watcher.trigger_dirs(&[self.current(), p]); 19 | } else { 20 | self.watcher.trigger_dirs(&[self.current()]); 21 | } 22 | 23 | self.peek(false); 24 | self.watch(()); 25 | self.update_paged((), tasks); 26 | 27 | tasks.prework_sorted(&self.current().files); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/seek.rs: -------------------------------------------------------------------------------- 1 | use yazi_config::YAZI; 2 | use yazi_plugin::isolate; 3 | use yazi_shared::event::{CmdCow, Data}; 4 | 5 | use crate::mgr::Mgr; 6 | 7 | #[derive(Debug)] 8 | struct Opt { 9 | units: i16, 10 | } 11 | 12 | impl From for Opt { 13 | fn from(c: CmdCow) -> Self { Self { units: c.first().and_then(Data::as_i16).unwrap_or(0) } } 14 | } 15 | 16 | impl Mgr { 17 | #[yazi_codegen::command] 18 | pub fn seek(&mut self, opt: Opt) { 19 | let Some(hovered) = self.hovered() else { 20 | return self.active_mut().preview.reset(); 21 | }; 22 | 23 | let Some(mime) = self.mimetype.by_file(hovered) else { 24 | return self.active_mut().preview.reset(); 25 | }; 26 | 27 | let Some(previewer) = YAZI.plugin.previewer(&hovered.url, mime) else { 28 | return self.active_mut().preview.reset(); 29 | }; 30 | 31 | isolate::seek_sync(&previewer.run, hovered.clone(), opt.units); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/spot.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::event::{CmdCow, Data}; 2 | 3 | use crate::mgr::Mgr; 4 | 5 | struct Opt { 6 | skip: Option, 7 | } 8 | 9 | impl From for Opt { 10 | fn from(c: CmdCow) -> Self { Self { skip: c.get("skip").and_then(Data::as_usize) } } 11 | } 12 | 13 | impl Mgr { 14 | #[yazi_codegen::command] 15 | pub fn spot(&mut self, opt: Opt) { 16 | let Some(hovered) = self.hovered().cloned() else { 17 | return; 18 | }; 19 | 20 | let mime = self.mimetype.by_file_owned(&hovered).unwrap_or_default(); 21 | // if !self.active().spot.same_file(&hovered, &mime) { 22 | // self.active_mut().spot.reset(); 23 | // } 24 | 25 | if let Some(skip) = opt.skip { 26 | self.active_mut().spot.skip = skip; 27 | } else if !self.active().spot.same_url(&hovered.url) { 28 | self.active_mut().spot.skip = 0; 29 | } 30 | 31 | self.active_mut().spot.go(hovered, mime); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/suspend.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::event::CmdCow; 2 | 3 | use crate::mgr::Mgr; 4 | 5 | impl Mgr { 6 | pub fn suspend(&mut self, _: CmdCow) { 7 | #[cfg(unix)] 8 | unsafe { 9 | libc::raise(libc::SIGTSTP); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/tab_close.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::{CmdCow, Data}; 3 | 4 | use crate::mgr::Tabs; 5 | 6 | struct Opt { 7 | idx: usize, 8 | } 9 | 10 | impl From for Opt { 11 | fn from(c: CmdCow) -> Self { Self { idx: c.first().and_then(Data::as_usize).unwrap_or(0) } } 12 | } 13 | 14 | impl From for Opt { 15 | fn from(idx: usize) -> Self { Self { idx } } 16 | } 17 | 18 | impl Tabs { 19 | #[yazi_codegen::command] 20 | pub fn close(&mut self, opt: Opt) { 21 | let len = self.items.len(); 22 | if len < 2 || opt.idx >= len { 23 | return; 24 | } 25 | 26 | self.items.remove(opt.idx).shutdown(); 27 | if opt.idx > self.cursor { 28 | self.set_idx(self.cursor); 29 | } else { 30 | self.set_idx(usize::min(self.cursor + 1, self.items.len() - 1)); 31 | } 32 | 33 | render!(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/tab_swap.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::{CmdCow, Data}; 3 | 4 | use crate::mgr::Tabs; 5 | 6 | struct Opt { 7 | step: isize, 8 | } 9 | 10 | impl From for Opt { 11 | fn from(c: CmdCow) -> Self { Self { step: c.first().and_then(Data::as_isize).unwrap_or(0) } } 12 | } 13 | 14 | impl Tabs { 15 | #[yazi_codegen::command] 16 | pub fn swap(&mut self, opt: Opt) { 17 | let idx = opt.step.saturating_add_unsigned(self.cursor).rem_euclid(self.items.len() as _) as _; 18 | if idx == self.cursor { 19 | return; 20 | } 21 | 22 | self.items.swap(self.cursor, idx); 23 | self.set_idx(idx); 24 | render!(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/tab_switch.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::{CmdCow, Data}; 3 | 4 | use crate::mgr::Tabs; 5 | 6 | struct Opt { 7 | step: isize, 8 | relative: bool, 9 | } 10 | 11 | impl From for Opt { 12 | fn from(c: CmdCow) -> Self { 13 | Self { step: c.first().and_then(Data::as_isize).unwrap_or(0), relative: c.bool("relative") } 14 | } 15 | } 16 | 17 | impl Tabs { 18 | #[yazi_codegen::command] 19 | pub fn switch(&mut self, opt: Opt) { 20 | let idx = if opt.relative { 21 | opt.step.saturating_add_unsigned(self.cursor).rem_euclid(self.items.len() as _) as _ 22 | } else { 23 | opt.step as usize 24 | }; 25 | 26 | if idx == self.cursor || idx >= self.items.len() { 27 | return; 28 | } 29 | 30 | self.set_idx(idx); 31 | render!(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/unyank.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::mgr::Mgr; 5 | 6 | struct Opt; 7 | 8 | impl From for Opt { 9 | fn from(_: CmdCow) -> Self { Self } 10 | } 11 | impl From<()> for Opt { 12 | fn from(_: ()) -> Self { Self } 13 | } 14 | 15 | impl Mgr { 16 | #[yazi_codegen::command] 17 | pub fn unyank(&mut self, _: Opt) { 18 | let repeek = self.hovered().is_some_and(|f| f.is_dir() && self.yanked.contains_in(&f.url)); 19 | self.yanked.clear(); 20 | 21 | render!(self.yanked.catchup_revision(false)); 22 | if repeek { 23 | self.peek(true); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/update_paged.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::{event::{CmdCow, Data}, url::Url}; 2 | 3 | use crate::{mgr::Mgr, tasks::Tasks}; 4 | 5 | #[derive(Default)] 6 | pub struct Opt { 7 | page: Option, 8 | only_if: Option, 9 | } 10 | 11 | impl From for Opt { 12 | fn from(mut c: CmdCow) -> Self { 13 | Self { page: c.first().and_then(Data::as_usize), only_if: c.take_url("only-if") } 14 | } 15 | } 16 | 17 | impl From<()> for Opt { 18 | fn from(_: ()) -> Self { Self::default() } 19 | } 20 | 21 | impl Mgr { 22 | pub fn update_paged(&mut self, opt: impl TryInto, tasks: &Tasks) { 23 | let Ok(opt): Result = opt.try_into() else { 24 | return; 25 | }; 26 | 27 | if opt.only_if.is_some_and(|u| u != *self.cwd()) { 28 | return; 29 | } 30 | 31 | let targets = self.current().paginate(opt.page.unwrap_or(self.current().page)); 32 | if !targets.is_empty() { 33 | tasks.fetch_paged(targets, &self.mimetype); 34 | tasks.preload_paged(targets, &self.mimetype); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/update_tasks.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::{event::CmdCow, url::Url}; 2 | 3 | use crate::mgr::Mgr; 4 | 5 | pub struct Opt { 6 | urls: Vec, 7 | } 8 | 9 | impl TryFrom for Opt { 10 | type Error = (); 11 | 12 | fn try_from(mut c: CmdCow) -> Result { 13 | Ok(Self { urls: c.take_any("urls").ok_or(())? }) 14 | } 15 | } 16 | 17 | impl Mgr { 18 | pub fn update_tasks(&mut self, opt: impl TryInto) { 19 | let Ok(opt) = opt.try_into() else { 20 | return; 21 | }; 22 | 23 | self.watcher.push_files(opt.urls); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/update_yanked.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use yazi_macro::render; 4 | use yazi_shared::{event::CmdCow, url::Url}; 5 | 6 | use crate::mgr::{Mgr, Yanked}; 7 | 8 | #[derive(Default)] 9 | pub struct Opt { 10 | cut: bool, 11 | urls: HashSet, 12 | } 13 | 14 | impl TryFrom for Opt { 15 | type Error = (); 16 | 17 | fn try_from(mut c: CmdCow) -> Result { 18 | if let Some(iter) = c.take_any::("urls") { 19 | Ok(Self { urls: iter.urls.into_iter().collect(), cut: iter.cut }) 20 | } else { 21 | Err(()) 22 | } 23 | } 24 | } 25 | 26 | impl Mgr { 27 | pub fn update_yanked(&mut self, opt: impl TryInto) { 28 | let Ok(opt) = opt.try_into() else { return }; 29 | 30 | if opt.urls.is_empty() && self.yanked.is_empty() { 31 | return; 32 | } 33 | 34 | self.yanked = Yanked::new(opt.cut, opt.urls); 35 | render!(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/watch.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use yazi_shared::event::CmdCow; 4 | 5 | use crate::mgr::Mgr; 6 | 7 | struct Opt; 8 | 9 | impl From for Opt { 10 | fn from(_: CmdCow) -> Self { Self } 11 | } 12 | impl From<()> for Opt { 13 | fn from((): ()) -> Self { Self } 14 | } 15 | 16 | impl Mgr { 17 | #[yazi_codegen::command] 18 | pub fn watch(&mut self, _: Opt) { 19 | let mut to_watch = HashSet::with_capacity(3 * self.tabs.len()); 20 | for tab in self.tabs.iter() { 21 | to_watch.insert(tab.cwd()); 22 | if let Some(ref p) = tab.parent { 23 | to_watch.insert(&p.url); 24 | } 25 | if let Some(h) = tab.hovered().filter(|&h| h.is_dir()) { 26 | to_watch.insert(&h.url); 27 | } 28 | } 29 | self.watcher.watch(to_watch); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/commands/yank.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::mgr::{Mgr, Yanked}; 5 | 6 | struct Opt { 7 | cut: bool, 8 | } 9 | 10 | impl From for Opt { 11 | fn from(c: CmdCow) -> Self { Self { cut: c.bool("cut") } } 12 | } 13 | 14 | impl Mgr { 15 | #[yazi_codegen::command] 16 | pub fn yank(&mut self, opt: Opt) { 17 | if !self.active_mut().try_escape_visual() { 18 | return; 19 | } 20 | 21 | self.yanked = Yanked::new(opt.cut, self.selected_or_hovered().cloned().collect()); 22 | render!(self.yanked.catchup_revision(true)); 23 | 24 | self.active_mut().escape_select(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /yazi-core/src/mgr/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_pub!(commands); 2 | 3 | yazi_macro::mod_flat!(linked mgr mimetype tabs watcher yanked); 4 | -------------------------------------------------------------------------------- /yazi-core/src/notify/commands/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(push tick); 2 | -------------------------------------------------------------------------------- /yazi-core/src/notify/commands/push.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | 3 | use yazi_macro::emit; 4 | use yazi_shared::event::Cmd; 5 | 6 | use crate::notify::{Message, Notify}; 7 | 8 | impl Notify { 9 | pub fn push(&mut self, msg: impl Into) { 10 | let mut msg = msg.into() as Message; 11 | 12 | let instant = Instant::now(); 13 | msg.timeout += instant - self.messages.first().map_or(instant, |m| m.instant); 14 | 15 | if self.messages.iter().all(|m| m != &msg) { 16 | self.messages.push(msg); 17 | emit!(Call(Cmd::args("app:update_notify", &[0]))); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /yazi-core/src/notify/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_pub!(commands); 2 | 3 | yazi_macro::mod_flat!(message notify); 4 | 5 | pub const NOTIFY_BORDER: u16 = 2; 6 | pub const NOTIFY_SPACING: u16 = 1; 7 | -------------------------------------------------------------------------------- /yazi-core/src/notify/notify.rs: -------------------------------------------------------------------------------- 1 | use std::ops::ControlFlow; 2 | 3 | use ratatui::layout::Rect; 4 | use tokio::task::JoinHandle; 5 | 6 | use super::{Message, NOTIFY_SPACING}; 7 | 8 | #[derive(Default)] 9 | pub struct Notify { 10 | pub(super) tick_handle: Option>, 11 | pub messages: Vec, 12 | } 13 | 14 | impl Notify { 15 | pub fn limit(&self, area: Rect) -> usize { 16 | if self.messages.is_empty() { 17 | return 0; 18 | } 19 | 20 | let mut height = area.height as usize; 21 | let flow = (0..self.messages.len().min(3)).try_fold(0, |acc, i| { 22 | match height.checked_sub(self.messages[i].height(area.width) + NOTIFY_SPACING as usize) { 23 | Some(h) => { 24 | height = h; 25 | ControlFlow::Continue(acc + 1) 26 | } 27 | None => ControlFlow::Break(acc), 28 | } 29 | }); 30 | 31 | 1.max(match flow { 32 | ControlFlow::Continue(i) => i, 33 | ControlFlow::Break(i) => i, 34 | }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /yazi-core/src/pick/commands/close.rs: -------------------------------------------------------------------------------- 1 | use anyhow::anyhow; 2 | use yazi_macro::render; 3 | use yazi_shared::event::CmdCow; 4 | 5 | use crate::pick::Pick; 6 | 7 | struct Opt { 8 | submit: bool, 9 | } 10 | 11 | impl From for Opt { 12 | fn from(c: CmdCow) -> Self { Self { submit: c.bool("submit") } } 13 | } 14 | impl From for Opt { 15 | fn from(submit: bool) -> Self { Self { submit } } 16 | } 17 | 18 | impl Pick { 19 | #[yazi_codegen::command] 20 | pub fn close(&mut self, opt: Opt) { 21 | if let Some(cb) = self.callback.take() { 22 | _ = cb.send(if opt.submit { Ok(self.cursor) } else { Err(anyhow!("canceled")) }); 23 | } 24 | 25 | self.cursor = 0; 26 | self.offset = 0; 27 | self.visible = false; 28 | render!(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /yazi-core/src/pick/commands/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(arrow close show); 2 | -------------------------------------------------------------------------------- /yazi-core/src/pick/commands/show.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::oneshot; 2 | use yazi_config::popup::PickCfg; 3 | use yazi_macro::render; 4 | use yazi_shared::event::CmdCow; 5 | 6 | use crate::pick::Pick; 7 | 8 | pub struct Opt { 9 | cfg: PickCfg, 10 | tx: oneshot::Sender>, 11 | } 12 | 13 | impl TryFrom for Opt { 14 | type Error = (); 15 | 16 | fn try_from(mut c: CmdCow) -> Result { 17 | Ok(Self { cfg: c.take_any("cfg").ok_or(())?, tx: c.take_any("tx").ok_or(())? }) 18 | } 19 | } 20 | 21 | impl Pick { 22 | pub fn show(&mut self, opt: impl TryInto) { 23 | let Ok(opt) = opt.try_into() else { 24 | return; 25 | }; 26 | 27 | self.close(false); 28 | self.title = opt.cfg.title; 29 | self.items = opt.cfg.items; 30 | self.position = opt.cfg.position; 31 | 32 | self.callback = Some(opt.tx); 33 | self.visible = true; 34 | render!(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /yazi-core/src/pick/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_pub!(commands); 2 | 3 | yazi_macro::mod_flat!(pick); 4 | -------------------------------------------------------------------------------- /yazi-core/src/pick/pick.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use tokio::sync::oneshot::Sender; 3 | use yazi_config::{YAZI, popup::Position}; 4 | 5 | #[derive(Default)] 6 | pub struct Pick { 7 | pub(super) title: String, 8 | pub(super) items: Vec, 9 | pub position: Position, 10 | 11 | pub(super) offset: usize, 12 | pub(super) cursor: usize, 13 | pub(super) callback: Option>>, 14 | 15 | pub visible: bool, 16 | } 17 | 18 | impl Pick { 19 | #[inline] 20 | pub fn window(&self) -> &[String] { 21 | let end = (self.offset + self.limit()).min(self.items.len()); 22 | &self.items[self.offset..end] 23 | } 24 | 25 | #[inline] 26 | pub(super) fn limit(&self) -> usize { 27 | self.position.offset.height.saturating_sub(YAZI.pick.border()) as usize 28 | } 29 | } 30 | 31 | impl Pick { 32 | #[inline] 33 | pub fn title(&self) -> String { self.title.clone() } 34 | 35 | #[inline] 36 | pub fn rel_cursor(&self) -> usize { self.cursor - self.offset } 37 | } 38 | -------------------------------------------------------------------------------- /yazi-core/src/spot/commands/arrow.rs: -------------------------------------------------------------------------------- 1 | use yazi_fs::Step; 2 | use yazi_macro::render; 3 | use yazi_proxy::MgrProxy; 4 | use yazi_shared::event::CmdCow; 5 | 6 | use crate::spot::Spot; 7 | 8 | struct Opt { 9 | step: Step, 10 | } 11 | 12 | impl From for Opt { 13 | fn from(c: CmdCow) -> Self { 14 | Self { step: c.first().and_then(|d| d.try_into().ok()).unwrap_or_default() } 15 | } 16 | } 17 | 18 | impl Spot { 19 | #[yazi_codegen::command] 20 | pub fn arrow(&mut self, opt: Opt) { 21 | let Some(lock) = &mut self.lock else { return }; 22 | 23 | let new = opt.step.add(self.skip, lock.len().unwrap_or(u16::MAX as _), 0); 24 | let Some(old) = lock.selected() else { 25 | return MgrProxy::spot(Some(new)); 26 | }; 27 | 28 | lock.select(Some(new)); 29 | let new = lock.selected().unwrap(); 30 | 31 | self.skip = new; 32 | render!(new != old); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /yazi-core/src/spot/commands/close.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::event::CmdCow; 2 | 3 | use crate::spot::Spot; 4 | 5 | struct Opt; 6 | 7 | impl From for Opt { 8 | fn from(_: CmdCow) -> Self { Self } 9 | } 10 | impl From<()> for Opt { 11 | fn from(_: ()) -> Self { Self } 12 | } 13 | 14 | impl Spot { 15 | #[yazi_codegen::command] 16 | pub fn close(&mut self, _: Opt) { self.reset(); } 17 | } 18 | -------------------------------------------------------------------------------- /yazi-core/src/spot/commands/copy.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use yazi_plugin::CLIPBOARD; 4 | use yazi_shared::event::CmdCow; 5 | 6 | use crate::spot::Spot; 7 | 8 | struct Opt { 9 | r#type: Cow<'static, str>, 10 | } 11 | 12 | impl From for Opt { 13 | fn from(mut c: CmdCow) -> Self { Self { r#type: c.take_first_str().unwrap_or_default() } } 14 | } 15 | 16 | impl Spot { 17 | #[yazi_codegen::command] 18 | pub fn copy(&mut self, opt: Opt) { 19 | let Some(lock) = &self.lock else { return }; 20 | let Some(table) = lock.table() else { return }; 21 | 22 | let mut s = String::new(); 23 | match opt.r#type.as_ref() { 24 | "cell" => { 25 | let Some(cell) = table.selected_cell() else { return }; 26 | s = cell.to_string(); 27 | } 28 | "line" => { 29 | // TODO 30 | } 31 | _ => {} 32 | } 33 | 34 | futures::executor::block_on(CLIPBOARD.set(s)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /yazi-core/src/spot/commands/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(arrow close copy swipe); 2 | -------------------------------------------------------------------------------- /yazi-core/src/spot/commands/swipe.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use yazi_proxy::{MgrProxy, TabProxy}; 4 | use yazi_shared::event::CmdCow; 5 | 6 | use crate::spot::Spot; 7 | 8 | struct Opt { 9 | step: Cow<'static, str>, 10 | } 11 | 12 | impl From for Opt { 13 | fn from(mut c: CmdCow) -> Self { Self { step: c.take_first_str().unwrap_or_default() } } 14 | } 15 | 16 | impl Spot { 17 | #[yazi_codegen::command] 18 | pub fn swipe(&mut self, opt: Opt) { 19 | TabProxy::arrow(opt.step); 20 | MgrProxy::spot(None); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /yazi-core/src/spot/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_pub!(commands); 2 | 3 | yazi_macro::mod_flat!(spot); 4 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/arrow.rs: -------------------------------------------------------------------------------- 1 | use yazi_fs::Step; 2 | use yazi_macro::render; 3 | use yazi_proxy::MgrProxy; 4 | use yazi_shared::event::CmdCow; 5 | 6 | use crate::tab::Tab; 7 | 8 | struct Opt { 9 | step: Step, 10 | } 11 | 12 | impl From for Opt { 13 | fn from(c: CmdCow) -> Self { 14 | Self { step: c.first().and_then(|d| d.try_into().ok()).unwrap_or_default() } 15 | } 16 | } 17 | 18 | impl From for Opt { 19 | fn from(n: isize) -> Self { Self { step: n.into() } } 20 | } 21 | 22 | impl Tab { 23 | #[yazi_codegen::command] 24 | pub fn arrow(&mut self, opt: Opt) { 25 | if !self.current.arrow(opt.step) { 26 | return; 27 | } 28 | 29 | // Visual selection 30 | if let Some((start, items)) = self.mode.visual_mut() { 31 | let end = self.current.cursor; 32 | *items = (start.min(end)..=end.max(start)).collect(); 33 | } 34 | 35 | self.hover(None); 36 | MgrProxy::peek(false); 37 | MgrProxy::watch(); 38 | 39 | render!(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/back.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::event::CmdCow; 2 | 3 | use crate::tab::Tab; 4 | 5 | impl Tab { 6 | pub fn back(&mut self, _: CmdCow) { 7 | if self.current.url.is_regular() { 8 | self.backstack.push(&self.current.url); 9 | } 10 | if let Some(u) = self.backstack.shift_backward().cloned() { 11 | self.cd((u, super::cd::OptSource::Back)); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/enter.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::event::CmdCow; 2 | 3 | use crate::tab::Tab; 4 | 5 | impl Tab { 6 | pub fn enter(&mut self, _: CmdCow) { 7 | self 8 | .hovered() 9 | .filter(|h| h.is_dir()) 10 | .map(|h| h.url.to_regular()) 11 | .map(|u| self.cd((u, super::cd::OptSource::Enter))); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/filter_do.rs: -------------------------------------------------------------------------------- 1 | use yazi_fs::Filter; 2 | use yazi_macro::render; 3 | use yazi_proxy::MgrProxy; 4 | 5 | use super::filter::Opt; 6 | use crate::tab::Tab; 7 | 8 | impl Tab { 9 | #[yazi_codegen::command] 10 | pub fn filter_do(&mut self, opt: Opt) { 11 | let filter = if opt.query.is_empty() { 12 | None 13 | } else if let Ok(f) = Filter::new(&opt.query, opt.case) { 14 | Some(f) 15 | } else { 16 | return; 17 | }; 18 | 19 | if opt.done { 20 | MgrProxy::update_paged(); // Update for paged files in next loop 21 | } 22 | 23 | let hovered = self.hovered().map(|f| f.urn_owned()); 24 | if !self.current.files.set_filter(filter) { 25 | return; 26 | } 27 | 28 | self.current.repos(hovered.as_ref()); 29 | if self.hovered().map(|f| f.urn()) != hovered.as_ref().map(|u| u.as_urn()) { 30 | self.hover(None); 31 | MgrProxy::peek(false); 32 | MgrProxy::watch(); 33 | } 34 | 35 | render!(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/find_arrow.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::tab::Tab; 5 | 6 | struct Opt { 7 | prev: bool, 8 | } 9 | 10 | impl From for Opt { 11 | fn from(c: CmdCow) -> Self { Self { prev: c.bool("previous") } } 12 | } 13 | 14 | impl Tab { 15 | #[yazi_codegen::command] 16 | pub fn find_arrow(&mut self, opt: Opt) { 17 | let Some(finder) = &mut self.finder else { 18 | return; 19 | }; 20 | 21 | render!(finder.catchup(&self.current.files)); 22 | if opt.prev { 23 | finder.prev(&self.current.files, self.current.cursor, false).map(|s| self.arrow(s)); 24 | } else { 25 | finder.next(&self.current.files, self.current.cursor, false).map(|s| self.arrow(s)); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/find_do.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | 3 | use super::find::Opt; 4 | use crate::tab::{Finder, Tab}; 5 | 6 | impl Tab { 7 | #[yazi_codegen::command] 8 | pub fn find_do(&mut self, opt: Opt) { 9 | let Some(query) = opt.query else { 10 | return; 11 | }; 12 | if query.is_empty() { 13 | self.escape_find(); 14 | return; 15 | } 16 | 17 | let Ok(finder) = Finder::new(&query, opt.case) else { 18 | return; 19 | }; 20 | if matches!(&self.finder, Some(f) if f.filter == finder.filter) { 21 | return; 22 | } 23 | 24 | let step = if opt.prev { 25 | finder.prev(&self.current.files, self.current.cursor, true) 26 | } else { 27 | finder.next(&self.current.files, self.current.cursor, true) 28 | }; 29 | 30 | if let Some(step) = step { 31 | self.arrow(step); 32 | } 33 | 34 | self.finder = Some(finder); 35 | render!(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/follow.rs: -------------------------------------------------------------------------------- 1 | use yazi_fs::clean_url; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::tab::Tab; 5 | 6 | struct Opt; 7 | 8 | impl From for Opt { 9 | fn from(_: CmdCow) -> Self { Self } 10 | } 11 | 12 | impl Tab { 13 | #[yazi_codegen::command] 14 | pub fn follow(&mut self, _: Opt) { 15 | let Some(file) = self.hovered() else { return }; 16 | let Some(link_to) = &file.link_to else { return }; 17 | 18 | if link_to.is_absolute() { 19 | self.reveal(link_to.to_owned()); 20 | } else if let Some(p) = file.url.parent_url() { 21 | self.reveal(clean_url(&p.join(link_to))); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/forward.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::event::CmdCow; 2 | 3 | use crate::tab::Tab; 4 | 5 | impl Tab { 6 | pub fn forward(&mut self, _: CmdCow) { 7 | if self.current.url.is_regular() { 8 | self.backstack.push(&self.current.url); 9 | } 10 | if let Some(u) = self.backstack.shift_forward().cloned() { 11 | self.cd((u, super::cd::OptSource::Forward)); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/hidden.rs: -------------------------------------------------------------------------------- 1 | use yazi_proxy::MgrProxy; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::tab::Tab; 5 | 6 | impl Tab { 7 | pub fn hidden(&mut self, mut c: CmdCow) { 8 | self.pref.show_hidden = match c.take_first_str().as_deref() { 9 | Some("show") => true, 10 | Some("hide") => false, 11 | _ => !self.pref.show_hidden, 12 | }; 13 | 14 | let hovered = self.hovered().map(|f| f.url_owned()); 15 | self.apply_files_attrs(); 16 | 17 | if hovered.as_ref() != self.hovered().map(|f| &f.url) { 18 | self.hover(hovered); 19 | MgrProxy::peek(false); 20 | MgrProxy::watch(); 21 | } else if self.hovered().is_some_and(|f| f.is_dir()) { 22 | MgrProxy::peek(true); 23 | } 24 | MgrProxy::update_paged(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/leave.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::event::CmdCow; 2 | 3 | use crate::tab::Tab; 4 | 5 | struct Opt; 6 | impl From<()> for Opt { 7 | fn from(_: ()) -> Self { Self } 8 | } 9 | impl From for Opt { 10 | fn from(_: CmdCow) -> Self { Self } 11 | } 12 | 13 | impl Tab { 14 | #[yazi_codegen::command] 15 | pub fn leave(&mut self, _: Opt) { 16 | self 17 | .current 18 | .hovered() 19 | .and_then(|h| h.url.parent_url()) 20 | .filter(|u| u != self.cwd()) 21 | .or_else(|| self.cwd().parent_url()) 22 | .map(|u| self.cd((u.into_regular(), super::cd::OptSource::Leave))); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/linemode.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::tab::Tab; 5 | 6 | impl Tab { 7 | pub fn linemode(&mut self, mut c: CmdCow) { 8 | let Some(new) = c.take_first_str() else { return }; 9 | if new == self.pref.linemode { 10 | return; 11 | } else if new.is_empty() || new.len() > 20 { 12 | return; 13 | } 14 | 15 | self.pref.linemode = new.into_owned(); 16 | render!(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!( 2 | arrow 3 | back 4 | cd 5 | copy 6 | enter 7 | escape 8 | filter 9 | filter_do 10 | find 11 | find_arrow 12 | find_do 13 | follow 14 | forward 15 | hidden 16 | hover 17 | leave 18 | linemode 19 | reveal 20 | search 21 | shell 22 | sort 23 | toggle 24 | toggle_all 25 | update_peeked 26 | update_spotted 27 | visual_mode 28 | ); 29 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/sort.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use yazi_fs::SortBy; 4 | use yazi_proxy::MgrProxy; 5 | use yazi_shared::event::CmdCow; 6 | 7 | use crate::{tab::Tab, tasks::Tasks}; 8 | 9 | impl Tab { 10 | pub fn sort(&mut self, c: CmdCow, tasks: &Tasks) { 11 | let mut new = self.pref.clone(); 12 | new.sort_by = c.first_str().and_then(|s| SortBy::from_str(s).ok()).unwrap_or(new.sort_by); 13 | new.sort_reverse = c.maybe_bool("reverse").unwrap_or(new.sort_reverse); 14 | new.sort_dir_first = c.maybe_bool("dir-first").unwrap_or(new.sort_dir_first); 15 | new.sort_sensitive = c.maybe_bool("sensitive").unwrap_or(new.sort_sensitive); 16 | new.sort_translit = c.maybe_bool("translit").unwrap_or(new.sort_translit); 17 | 18 | if new == self.pref { 19 | return; 20 | } 21 | 22 | self.pref = new; 23 | self.apply_files_attrs(); 24 | self.hover(None); 25 | tasks.prework_sorted(&self.current.files); 26 | 27 | MgrProxy::peek(false); 28 | MgrProxy::watch(); 29 | MgrProxy::update_paged(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/update_peeked.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_plugin::utils::PreviewLock; 3 | use yazi_shared::event::CmdCow; 4 | 5 | use crate::tab::Tab; 6 | 7 | pub struct Opt { 8 | lock: PreviewLock, 9 | } 10 | 11 | impl TryFrom for Opt { 12 | type Error = (); 13 | 14 | fn try_from(mut c: CmdCow) -> Result { 15 | Ok(Self { lock: c.take_any("lock").ok_or(())? }) 16 | } 17 | } 18 | 19 | impl Tab { 20 | pub fn update_peeked(&mut self, opt: impl TryInto) { 21 | let Some(hovered) = self.hovered().map(|h| &h.url) else { 22 | return self.preview.reset(); 23 | }; 24 | 25 | let Ok(opt) = opt.try_into() else { 26 | return; 27 | }; 28 | 29 | if opt.lock.url != *hovered { 30 | return; 31 | } 32 | 33 | self.preview.lock = Some(opt.lock); 34 | render!(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/update_spotted.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_plugin::utils::SpotLock; 3 | use yazi_shared::event::CmdCow; 4 | 5 | use crate::tab::Tab; 6 | 7 | pub struct Opt { 8 | lock: SpotLock, 9 | } 10 | 11 | impl TryFrom for Opt { 12 | type Error = (); 13 | 14 | fn try_from(mut c: CmdCow) -> Result { 15 | Ok(Self { lock: c.take_any("lock").ok_or(())? }) 16 | } 17 | } 18 | 19 | impl Tab { 20 | pub fn update_spotted(&mut self, opt: impl TryInto) { 21 | let Some(hovered) = self.hovered().map(|h| &h.url) else { 22 | return self.spot.reset(); 23 | }; 24 | 25 | let Ok(mut opt): Result = opt.try_into() else { 26 | return; 27 | }; 28 | 29 | if opt.lock.url != *hovered { 30 | return; 31 | } 32 | 33 | if self.spot.lock.as_ref().is_none_or(|l| l.id != opt.lock.id) { 34 | self.spot.skip = opt.lock.selected().unwrap_or_default(); 35 | } else if let Some(s) = opt.lock.selected() { 36 | self.spot.skip = s; 37 | } else { 38 | opt.lock.select(Some(self.spot.skip)); 39 | } 40 | 41 | self.spot.lock = Some(opt.lock); 42 | render!(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /yazi-core/src/tab/commands/visual_mode.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeSet; 2 | 3 | use yazi_macro::render; 4 | use yazi_shared::event::CmdCow; 5 | 6 | use crate::tab::{Mode, Tab}; 7 | 8 | struct Opt { 9 | unset: bool, 10 | } 11 | 12 | impl From for Opt { 13 | fn from(c: CmdCow) -> Self { Self { unset: c.bool("unset") } } 14 | } 15 | 16 | impl Tab { 17 | #[yazi_codegen::command] 18 | pub fn visual_mode(&mut self, opt: Opt) { 19 | let idx = self.current.cursor; 20 | if opt.unset { 21 | self.mode = Mode::Unset(idx, BTreeSet::from([idx])); 22 | } else { 23 | self.mode = Mode::Select(idx, BTreeSet::from([idx])); 24 | }; 25 | render!(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /yazi-core/src/tab/history.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, ops::{Deref, DerefMut}}; 2 | 3 | use yazi_shared::url::Url; 4 | 5 | use super::Folder; 6 | 7 | #[derive(Default)] 8 | pub struct History(HashMap); 9 | 10 | impl Deref for History { 11 | type Target = HashMap; 12 | 13 | #[inline] 14 | fn deref(&self) -> &Self::Target { &self.0 } 15 | } 16 | 17 | impl DerefMut for History { 18 | #[inline] 19 | fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } 20 | } 21 | 22 | impl History { 23 | #[inline] 24 | pub fn remove_or(&mut self, url: &Url) -> Folder { 25 | self.0.remove(url).unwrap_or_else(|| Folder::from(url)) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /yazi-core/src/tab/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_pub!(commands); 2 | 3 | yazi_macro::mod_flat!(backstack finder folder history mode preference preview selected tab); 4 | -------------------------------------------------------------------------------- /yazi-core/src/tasks/commands/arrow.rs: -------------------------------------------------------------------------------- 1 | use yazi_fs::Step; 2 | use yazi_macro::render; 3 | use yazi_shared::event::CmdCow; 4 | 5 | use crate::tasks::Tasks; 6 | 7 | struct Opt { 8 | step: Step, 9 | } 10 | 11 | impl From for Opt { 12 | fn from(c: CmdCow) -> Self { 13 | Self { step: c.first().and_then(|d| d.try_into().ok()).unwrap_or_default() } 14 | } 15 | } 16 | 17 | impl From for Opt { 18 | fn from(n: isize) -> Self { Self { step: n.into() } } 19 | } 20 | 21 | impl Tasks { 22 | #[yazi_codegen::command] 23 | pub fn arrow(&mut self, opt: Opt) { 24 | let old = self.cursor; 25 | self.cursor = opt.step.add(self.cursor, self.summaries.len(), Self::limit()); 26 | 27 | render!(self.cursor != old); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /yazi-core/src/tasks/commands/cancel.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::tasks::Tasks; 5 | 6 | impl Tasks { 7 | pub fn cancel(&mut self, _: CmdCow) { 8 | let id = self.ongoing().lock().get_id(self.cursor); 9 | if id.map(|id| self.scheduler.cancel(id)) != Some(true) { 10 | return; 11 | } 12 | 13 | self.summaries = self.paginate(); 14 | self.arrow(0); 15 | render!(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /yazi-core/src/tasks/commands/close.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::tasks::Tasks; 5 | 6 | struct Opt; 7 | 8 | impl From for Opt { 9 | fn from(_: CmdCow) -> Self { Self } 10 | } 11 | impl From<()> for Opt { 12 | fn from(_: ()) -> Self { Self } 13 | } 14 | 15 | impl Tasks { 16 | #[yazi_codegen::command] 17 | pub fn close(&mut self, _: Opt) { 18 | if !self.visible { 19 | return; 20 | } 21 | 22 | self.visible = false; 23 | self.summaries = Vec::new(); 24 | 25 | self.arrow(0); 26 | render!(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /yazi-core/src/tasks/commands/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(arrow cancel close inspect open_with process_exec show); 2 | -------------------------------------------------------------------------------- /yazi-core/src/tasks/commands/open_with.rs: -------------------------------------------------------------------------------- 1 | use yazi_proxy::options::OpenWithOpt; 2 | 3 | use crate::tasks::Tasks; 4 | 5 | impl Tasks { 6 | pub fn open_with(&mut self, opt: impl TryInto) { 7 | if let Ok(opt) = opt.try_into() { 8 | self.process_from_opener( 9 | opt.cwd, 10 | opt.opener, 11 | opt.targets.into_iter().map(|u| u.into_path().into_os_string()).collect(), 12 | ); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /yazi-core/src/tasks/commands/process_exec.rs: -------------------------------------------------------------------------------- 1 | use yazi_proxy::options::ProcessExecOpt; 2 | 3 | use crate::tasks::Tasks; 4 | 5 | impl Tasks { 6 | pub fn process_exec(&mut self, opt: impl TryInto) { 7 | if let Ok(opt) = opt.try_into() { 8 | self.scheduler.process_open(opt); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /yazi-core/src/tasks/commands/show.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::tasks::Tasks; 5 | 6 | struct Opt; 7 | 8 | impl From for Opt { 9 | fn from(_: CmdCow) -> Self { Self } 10 | } 11 | impl From<()> for Opt { 12 | fn from(_: ()) -> Self { Self } 13 | } 14 | 15 | impl Tasks { 16 | #[yazi_codegen::command] 17 | pub fn show(&mut self, _: Opt) { 18 | if self.visible { 19 | return; 20 | } 21 | 22 | self.visible = true; 23 | self.summaries = self.paginate(); 24 | 25 | self.arrow(0); 26 | render!(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /yazi-core/src/tasks/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_pub!(commands); 2 | 3 | yazi_macro::mod_flat!(file plugin preload process progress tasks); 4 | 5 | pub const TASKS_BORDER: u16 = 2; 6 | pub const TASKS_PADDING: u16 = 2; 7 | pub const TASKS_PERCENT: u16 = 80; 8 | -------------------------------------------------------------------------------- /yazi-core/src/tasks/plugin.rs: -------------------------------------------------------------------------------- 1 | use yazi_proxy::options::PluginOpt; 2 | 3 | use super::Tasks; 4 | 5 | impl Tasks { 6 | #[inline] 7 | pub fn plugin_micro(&self, opt: PluginOpt) { self.scheduler.plugin_micro(opt); } 8 | 9 | #[inline] 10 | pub fn plugin_macro(&self, opt: PluginOpt) { self.scheduler.plugin_macro(opt); } 11 | } 12 | -------------------------------------------------------------------------------- /yazi-core/src/tasks/progress.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | use yazi_scheduler::Ongoing; 3 | 4 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)] 5 | pub struct TasksProgress { 6 | pub total: u32, 7 | pub succ: u32, 8 | pub fail: u32, 9 | 10 | pub found: u64, 11 | pub processed: u64, 12 | } 13 | 14 | impl From<&Ongoing> for TasksProgress { 15 | fn from(ongoing: &Ongoing) -> Self { 16 | let mut progress = Self::default(); 17 | if ongoing.is_empty() { 18 | return progress; 19 | } 20 | 21 | for task in ongoing.values() { 22 | progress.total += task.total; 23 | progress.succ += task.succ; 24 | progress.fail += task.fail; 25 | 26 | progress.found += task.found; 27 | progress.processed += task.processed; 28 | } 29 | progress 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /yazi-core/src/which/commands/callback.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::mpsc; 2 | use tracing::error; 3 | use yazi_shared::event::{CmdCow, Data}; 4 | 5 | use crate::which::Which; 6 | 7 | pub struct Opt { 8 | tx: mpsc::Sender, 9 | idx: usize, 10 | } 11 | 12 | impl TryFrom for Opt { 13 | type Error = (); 14 | 15 | fn try_from(mut c: CmdCow) -> Result { 16 | Ok(Self { 17 | tx: c.take_any("tx").ok_or(())?, 18 | idx: c.first().and_then(Data::as_usize).ok_or(())?, 19 | }) 20 | } 21 | } 22 | 23 | impl Which { 24 | pub fn callback(&mut self, opt: impl TryInto) { 25 | let Ok(opt) = opt.try_into() else { 26 | return; 27 | }; 28 | 29 | if opt.tx.try_send(opt.idx).is_err() { 30 | error!("which callback: send error"); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /yazi-core/src/which/commands/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(callback show); 2 | -------------------------------------------------------------------------------- /yazi-core/src/which/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_pub!(commands); 2 | 3 | yazi_macro::mod_flat!(sorter which); 4 | -------------------------------------------------------------------------------- /yazi-core/src/which/which.rs: -------------------------------------------------------------------------------- 1 | use yazi_config::keymap::{ChordCow, Key}; 2 | use yazi_macro::{emit, render_and}; 3 | 4 | #[derive(Default)] 5 | pub struct Which { 6 | pub times: usize, 7 | pub cands: Vec, 8 | 9 | // Visibility 10 | pub visible: bool, 11 | pub silent: bool, 12 | } 13 | 14 | impl Which { 15 | pub fn r#type(&mut self, key: Key) -> bool { 16 | self.cands.retain(|c| c.on.len() > self.times && c.on[self.times] == key); 17 | self.times += 1; 18 | 19 | if self.cands.is_empty() { 20 | self.reset(); 21 | } else if self.cands.len() == 1 { 22 | emit!(Seq(self.cands.remove(0).into_seq())); 23 | self.reset(); 24 | } else if let Some(i) = self.cands.iter().position(|c| c.on.len() == self.times) { 25 | emit!(Seq(self.cands.remove(i).into_seq())); 26 | self.reset(); 27 | } 28 | 29 | render_and!(true) 30 | } 31 | 32 | fn reset(&mut self) { 33 | self.times = 0; 34 | self.cands.clear(); 35 | 36 | self.visible = false; 37 | self.silent = false; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /yazi-dds/build.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use vergen_gitcl::{Emitter, GitclBuilder}; 4 | 5 | fn main() -> Result<(), Box> { 6 | Emitter::default() 7 | .add_instructions(&GitclBuilder::default().commit_date(true).sha(true).build()?)? 8 | .emit()?; 9 | 10 | Ok(()) 11 | } 12 | -------------------------------------------------------------------------------- /yazi-dds/src/body/bye.rs: -------------------------------------------------------------------------------- 1 | use mlua::{ExternalResult, IntoLua, Lua, Value}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use super::Body; 5 | 6 | #[derive(Debug, Serialize, Deserialize)] 7 | pub struct BodyBye; 8 | 9 | impl BodyBye { 10 | #[inline] 11 | pub fn owned() -> Body<'static> { Self.into() } 12 | } 13 | 14 | impl From for Body<'_> { 15 | fn from(value: BodyBye) -> Self { Self::Bye(value) } 16 | } 17 | 18 | impl IntoLua for BodyBye { 19 | fn into_lua(self, _: &Lua) -> mlua::Result { 20 | Err("BodyBye cannot be converted to Lua").into_lua_err() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /yazi-dds/src/body/delete.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use mlua::{IntoLua, Lua, Value}; 4 | use serde::{Deserialize, Serialize}; 5 | use yazi_shared::url::Url; 6 | 7 | use super::Body; 8 | 9 | #[derive(Debug, Serialize, Deserialize)] 10 | pub struct BodyDelete<'a> { 11 | pub urls: Cow<'a, Vec>, 12 | } 13 | 14 | impl<'a> BodyDelete<'a> { 15 | #[inline] 16 | pub fn borrowed(urls: &'a Vec) -> Body<'a> { Self { urls: Cow::Borrowed(urls) }.into() } 17 | } 18 | 19 | impl BodyDelete<'static> { 20 | #[inline] 21 | pub fn owned(urls: Vec) -> Body<'static> { Self { urls: Cow::Owned(urls) }.into() } 22 | } 23 | 24 | impl<'a> From> for Body<'a> { 25 | fn from(value: BodyDelete<'a>) -> Self { Self::Delete(value) } 26 | } 27 | 28 | impl IntoLua for BodyDelete<'static> { 29 | fn into_lua(self, lua: &Lua) -> mlua::Result { 30 | let urls = 31 | lua.create_sequence_from(self.urls.into_owned().into_iter().map(yazi_binding::Url::new))?; 32 | 33 | lua.create_table_from([("urls", urls)])?.into_lua(lua) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /yazi-dds/src/body/hey.rs: -------------------------------------------------------------------------------- 1 | use std::{borrow::Cow, collections::HashMap}; 2 | 3 | use mlua::{ExternalResult, IntoLua, Lua, Value}; 4 | use serde::{Deserialize, Serialize}; 5 | use yazi_shared::Id; 6 | 7 | use super::{Body, BodyHi}; 8 | use crate::Peer; 9 | 10 | #[derive(Debug, Serialize, Deserialize)] 11 | pub struct BodyHey { 12 | pub peers: HashMap, 13 | pub version: Cow<'static, str>, 14 | } 15 | 16 | impl BodyHey { 17 | #[inline] 18 | pub fn owned(peers: HashMap) -> Body<'static> { 19 | Self { peers, version: BodyHi::version().into() }.into() 20 | } 21 | } 22 | 23 | impl From for Body<'_> { 24 | fn from(value: BodyHey) -> Self { Self::Hey(value) } 25 | } 26 | 27 | impl IntoLua for BodyHey { 28 | fn into_lua(self, _: &Lua) -> mlua::Result { 29 | Err("BodyHey cannot be converted to Lua").into_lua_err() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /yazi-dds/src/body/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!( 4 | body bulk bye cd custom delete hey hi hover load mount r#move rename tab trash yank 5 | ); 6 | -------------------------------------------------------------------------------- /yazi-dds/src/body/mount.rs: -------------------------------------------------------------------------------- 1 | use mlua::{IntoLua, Lua, Value}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use super::Body; 5 | 6 | #[derive(Debug, Serialize, Deserialize)] 7 | pub struct BodyMount; 8 | 9 | impl BodyMount { 10 | #[inline] 11 | pub fn owned() -> Body<'static> { Self.into() } 12 | } 13 | 14 | impl From for Body<'_> { 15 | fn from(value: BodyMount) -> Self { Self::Mount(value) } 16 | } 17 | 18 | impl IntoLua for BodyMount { 19 | fn into_lua(self, _: &Lua) -> mlua::Result { Ok(Value::Nil) } 20 | } 21 | -------------------------------------------------------------------------------- /yazi-dds/src/body/tab.rs: -------------------------------------------------------------------------------- 1 | use mlua::{IntoLua, Lua, Value}; 2 | use serde::{Deserialize, Serialize}; 3 | use yazi_shared::Id; 4 | 5 | use super::Body; 6 | 7 | #[derive(Debug, Serialize, Deserialize)] 8 | pub struct BodyTab { 9 | pub id: Id, 10 | } 11 | 12 | impl BodyTab { 13 | #[inline] 14 | pub fn owned(id: Id) -> Body<'static> { Self { id }.into() } 15 | } 16 | 17 | impl From for Body<'_> { 18 | fn from(value: BodyTab) -> Self { Self::Tab(value) } 19 | } 20 | 21 | impl IntoLua for BodyTab { 22 | fn into_lua(self, lua: &Lua) -> mlua::Result { 23 | lua.create_table_from([("idx", self.id.get())])?.into_lua(lua) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /yazi-dds/src/body/trash.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use mlua::{IntoLua, Lua, Value}; 4 | use serde::{Deserialize, Serialize}; 5 | use yazi_shared::url::Url; 6 | 7 | use super::Body; 8 | 9 | #[derive(Debug, Serialize, Deserialize)] 10 | pub struct BodyTrash<'a> { 11 | pub urls: Cow<'a, Vec>, 12 | } 13 | 14 | impl<'a> BodyTrash<'a> { 15 | #[inline] 16 | pub fn borrowed(urls: &'a Vec) -> Body<'a> { Self { urls: Cow::Borrowed(urls) }.into() } 17 | } 18 | 19 | impl BodyTrash<'static> { 20 | #[inline] 21 | pub fn owned(urls: Vec) -> Body<'static> { Self { urls: Cow::Owned(urls) }.into() } 22 | } 23 | 24 | impl<'a> From> for Body<'a> { 25 | fn from(value: BodyTrash<'a>) -> Self { Self::Trash(value) } 26 | } 27 | 28 | impl IntoLua for BodyTrash<'static> { 29 | fn into_lua(self, lua: &Lua) -> mlua::Result { 30 | let urls = 31 | lua.create_sequence_from(self.urls.into_owned().into_iter().map(yazi_binding::Url::new))?; 32 | 33 | lua.create_table_from([("urls", urls)])?.into_lua(lua) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /yazi-dds/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::option_map_unit_fn)] 2 | 3 | yazi_macro::mod_pub!(body); 4 | 5 | yazi_macro::mod_flat!(client payload pubsub pump sendable server state stream); 6 | 7 | pub fn init() { 8 | let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); 9 | 10 | // Client 11 | ID.init(yazi_boot::ARGS.client_id.unwrap_or(yazi_shared::Id::unique())); 12 | PEERS.with(<_>::default); 13 | QUEUE_TX.init(tx); 14 | QUEUE_RX.init(rx); 15 | 16 | // Server 17 | CLIENTS.with(<_>::default); 18 | STATE.with(<_>::default); 19 | 20 | // Pubsub 21 | LOCAL.with(<_>::default); 22 | REMOTE.with(<_>::default); 23 | 24 | // Env 25 | unsafe { 26 | if let Some(s) = std::env::var("YAZI_ID").ok().filter(|s| !s.is_empty()) { 27 | std::env::set_var("YAZI_PID", s); 28 | } 29 | std::env::set_var("YAZI_ID", ID.to_string()); 30 | std::env::set_var( 31 | "YAZI_LEVEL", 32 | (std::env::var("YAZI_LEVEL").unwrap_or_default().parse().unwrap_or(0u16) + 1).to_string(), 33 | ); 34 | } 35 | } 36 | 37 | pub fn serve() { 38 | Pump::serve(); 39 | Client::serve(); 40 | } 41 | 42 | pub async fn shutdown() { Pump::shutdown().await; } 43 | -------------------------------------------------------------------------------- /yazi-ffi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yazi-ffi" 3 | version = "25.5.31" 4 | edition = "2024" 5 | license = "MIT" 6 | authors = [ "sxyazi " ] 7 | description = "Yazi foreign function interface" 8 | homepage = "https://yazi-rs.github.io" 9 | repository = "https://github.com/sxyazi/yazi" 10 | 11 | [dependencies] 12 | yazi-macro = { path = "../yazi-macro", version = "25.5.31" } 13 | 14 | # External dependencies 15 | anyhow = { workspace = true } 16 | 17 | [target."cfg(unix)".dependencies] 18 | libc = { workspace = true } 19 | 20 | [target.'cfg(target_os = "macos")'.dependencies] 21 | core-foundation-sys = { workspace = true } 22 | objc = { workspace = true } 23 | -------------------------------------------------------------------------------- /yazi-ffi/src/io_kit.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_char; 2 | 3 | use core_foundation_sys::{base::{CFAllocatorRef, CFTypeRef, mach_port_t}, dictionary::CFMutableDictionaryRef, string::CFStringRef}; 4 | use libc::kern_return_t; 5 | 6 | #[link(name = "IOKit", kind = "framework")] 7 | unsafe extern "C" { 8 | pub fn IOServiceGetMatchingServices( 9 | mainPort: mach_port_t, 10 | matching: CFMutableDictionaryRef, 11 | existing: *mut mach_port_t, 12 | ) -> kern_return_t; 13 | 14 | pub fn IOServiceMatching(a: *const c_char) -> CFMutableDictionaryRef; 15 | 16 | pub fn IOIteratorNext(iterator: mach_port_t) -> mach_port_t; 17 | 18 | pub fn IORegistryEntryCreateCFProperty( 19 | entry: mach_port_t, 20 | key: CFStringRef, 21 | allocator: CFAllocatorRef, 22 | options: u32, 23 | ) -> CFTypeRef; 24 | 25 | pub fn IOObjectRelease(obj: mach_port_t) -> kern_return_t; 26 | } 27 | -------------------------------------------------------------------------------- /yazi-ffi/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_os = "macos")] 2 | yazi_macro::mod_flat!(cf_dict cf_string disk_arbitration io_kit); 3 | -------------------------------------------------------------------------------- /yazi-fm/build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, error::Error}; 2 | 3 | fn main() -> Result<(), Box> { 4 | let dir = env::var("OUT_DIR").unwrap(); 5 | 6 | // cargo build 7 | // C:\Users\Ika\Desktop\yazi\target\release\build\yazi-fm-cfc94820f71daa30\out 8 | // cargo install 9 | // C:\Users\Ika\AppData\Local\Temp\cargo-installTFU8cj\release\build\ 10 | // yazi-fm-45dffef2500eecd0\out 11 | 12 | if dir.contains("\\release\\build\\yazi-fm-") { 13 | panic!( 14 | "Unwinding must be enabled for Windows. Please use `cargo build --profile release-windows --locked` instead to build Yazi." 15 | ); 16 | } 17 | 18 | Ok(()) 19 | } 20 | -------------------------------------------------------------------------------- /yazi-fm/src/app/commands/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!( 2 | accept_payload 3 | mouse 4 | notify 5 | plugin 6 | quit 7 | reflow 8 | render 9 | resize 10 | resume 11 | stop 12 | update_notify 13 | update_progress 14 | ); 15 | -------------------------------------------------------------------------------- /yazi-fm/src/app/commands/notify.rs: -------------------------------------------------------------------------------- 1 | use yazi_proxy::options::NotifyOpt; 2 | 3 | use crate::app::App; 4 | 5 | impl App { 6 | pub(crate) fn notify(&mut self, opt: impl TryInto) { 7 | let Ok(opt) = opt.try_into() else { 8 | return; 9 | }; 10 | 11 | self.cx.notify.push(opt); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /yazi-fm/src/app/commands/quit.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::OsString; 2 | 3 | use tokio::fs; 4 | use yazi_boot::ARGS; 5 | use yazi_shared::event::EventQuit; 6 | 7 | use crate::{Term, app::App}; 8 | 9 | impl App { 10 | pub(crate) fn quit(&mut self, opt: EventQuit) -> ! { 11 | self.cx.tasks.shutdown(); 12 | self.cx.mgr.shutdown(); 13 | 14 | futures::executor::block_on(async { 15 | _ = futures::join!( 16 | yazi_dds::shutdown(), 17 | yazi_dds::STATE.drain(), 18 | self.cwd_to_file(opt.no_cwd_file), 19 | self.selected_to_file(opt.selected) 20 | ); 21 | }); 22 | 23 | Term::goodbye(|| opt.code); 24 | } 25 | 26 | async fn cwd_to_file(&self, no: bool) { 27 | if let Some(p) = ARGS.cwd_file.as_ref().filter(|_| !no) { 28 | let cwd = self.cx.mgr.cwd().as_os_str(); 29 | fs::write(p, cwd.as_encoded_bytes()).await.ok(); 30 | } 31 | } 32 | 33 | async fn selected_to_file(&self, selected: Option) { 34 | if let (Some(s), Some(p)) = (selected, &ARGS.chooser_file) { 35 | fs::write(p, s.as_encoded_bytes()).await.ok(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /yazi-fm/src/app/commands/resize.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::event::CmdCow; 2 | 3 | use crate::app::App; 4 | 5 | struct Opt; 6 | 7 | impl From for Opt { 8 | fn from(_: CmdCow) -> Self { Self } 9 | } 10 | 11 | impl From<()> for Opt { 12 | fn from(_: ()) -> Self { Self } 13 | } 14 | 15 | impl App { 16 | #[yazi_codegen::command] 17 | pub fn resize(&mut self, _: Opt) { 18 | self.cx.active_mut().preview.reset(); 19 | self.reflow(()); 20 | 21 | self.cx.current_mut().sync_page(true); 22 | self.cx.current_mut().arrow(0); 23 | self.cx.mgr.peek(false); 24 | self.cx.mgr.parent_mut().map(|f| f.arrow(0)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /yazi-fm/src/app/commands/resume.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::{Term, app::App}; 5 | 6 | impl App { 7 | pub(crate) fn resume(&mut self, _: CmdCow) { 8 | self.cx.active_mut().preview.reset_image(); 9 | self.term = Some(Term::start().unwrap()); 10 | 11 | // While the app resumes, it's possible that the terminal size has changed. 12 | // We need to trigger a resize, and render the UI based on the resized area. 13 | self.resize(()); 14 | 15 | self.signals.resume(None); 16 | 17 | render!(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /yazi-fm/src/app/commands/stop.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::oneshot; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::app::App; 5 | 6 | struct Opt { 7 | tx: Option>, 8 | } 9 | 10 | impl From for Opt { 11 | fn from(mut c: CmdCow) -> Self { Self { tx: c.take_any("tx") } } 12 | } 13 | 14 | impl App { 15 | #[yazi_codegen::command] 16 | pub fn stop(&mut self, opt: Opt) { 17 | self.cx.active_mut().preview.reset_image(); 18 | 19 | // We need to destroy the `term` first before stopping the `signals` 20 | // to prevent any signal from triggering the term to render again 21 | // while the app is being suspended. 22 | self.term = None; 23 | 24 | self.signals.stop(opt.tx); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /yazi-fm/src/app/commands/update_notify.rs: -------------------------------------------------------------------------------- 1 | use ratatui::layout::Rect; 2 | use yazi_adapter::Dimension; 3 | use yazi_shared::event::CmdCow; 4 | 5 | use crate::{app::App, notify}; 6 | 7 | impl App { 8 | pub(crate) fn update_notify(&mut self, cmd: CmdCow) { 9 | let Dimension { rows, columns, .. } = Dimension::available(); 10 | let area = 11 | notify::Notify::available(Rect { x: 0, y: 0, width: columns, height: rows }); 12 | 13 | self.cx.notify.tick(cmd, area); 14 | 15 | if self.cx.notify.messages.is_empty() { 16 | self.render(); 17 | } else { 18 | self.render_partially(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /yazi-fm/src/app/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_pub!(commands); 2 | 3 | yazi_macro::mod_flat!(app); 4 | -------------------------------------------------------------------------------- /yazi-fm/src/cmp/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(cmp); 2 | -------------------------------------------------------------------------------- /yazi-fm/src/confirm/buttons.rs: -------------------------------------------------------------------------------- 1 | use ratatui::{buffer::Buffer, layout::{Constraint, Rect}, text::Span, widgets::{Paragraph, Widget}}; 2 | use yazi_config::THEME; 3 | 4 | pub(crate) struct Buttons; 5 | 6 | impl Widget for Buttons { 7 | fn render(self, area: Rect, buf: &mut Buffer) { 8 | let chunks = 9 | ratatui::layout::Layout::horizontal([Constraint::Fill(1), Constraint::Fill(1)]).split(area); 10 | 11 | Paragraph::new(Span::raw(&THEME.confirm.btn_labels[0]).style(THEME.confirm.btn_yes)) 12 | .centered() 13 | .render(chunks[0], buf); 14 | Paragraph::new(Span::raw(&THEME.confirm.btn_labels[1]).style(THEME.confirm.btn_no)) 15 | .centered() 16 | .render(chunks[1], buf); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /yazi-fm/src/confirm/content.rs: -------------------------------------------------------------------------------- 1 | use ratatui::{buffer::Buffer, layout::{Margin, Rect}, style::Styled, widgets::{Block, Borders, Widget}}; 2 | use yazi_config::THEME; 3 | 4 | use crate::Ctx; 5 | 6 | pub(crate) struct Content<'a> { 7 | cx: &'a Ctx, 8 | border: bool, 9 | } 10 | 11 | impl<'a> Content<'a> { 12 | pub(crate) fn new(cx: &'a Ctx, border: bool) -> Self { Self { cx, border } } 13 | } 14 | 15 | impl Widget for Content<'_> { 16 | fn render(self, area: Rect, buf: &mut Buffer) { 17 | let confirm = &self.cx.confirm; 18 | 19 | // Content area 20 | let inner = area.inner(Margin::new(1, 0)); 21 | 22 | // Border 23 | let block = if self.border { 24 | Block::new().borders(Borders::BOTTOM).border_style(THEME.confirm.border) 25 | } else { 26 | Block::new() 27 | }; 28 | 29 | confirm 30 | .content 31 | .clone() 32 | .alignment(ratatui::layout::Alignment::Center) 33 | .block(block) 34 | .style(THEME.confirm.content.derive(Styled::style(&confirm.content))) 35 | .render(inner, buf); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /yazi-fm/src/confirm/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(buttons confirm content list); 2 | -------------------------------------------------------------------------------- /yazi-fm/src/help/help.rs: -------------------------------------------------------------------------------- 1 | use ratatui::{buffer::Buffer, layout::{self, Constraint, Rect}, text::Line, widgets::Widget}; 2 | use yazi_config::{KEYMAP, THEME}; 3 | 4 | use super::Bindings; 5 | use crate::Ctx; 6 | 7 | pub(crate) struct Help<'a> { 8 | cx: &'a Ctx, 9 | } 10 | 11 | impl<'a> Help<'a> { 12 | pub fn new(cx: &'a Ctx) -> Self { Self { cx } } 13 | 14 | fn tips() -> String { 15 | match KEYMAP.help.iter().find(|&c| c.run.iter().any(|c| c.name == "filter")) { 16 | Some(c) => format!(" (Press `{}` to filter)", c.on()), 17 | None => String::new(), 18 | } 19 | } 20 | } 21 | 22 | impl Widget for Help<'_> { 23 | fn render(self, area: Rect, buf: &mut Buffer) { 24 | let help = &self.cx.help; 25 | yazi_plugin::elements::Clear::default().render(area, buf); 26 | 27 | let chunks = layout::Layout::vertical([Constraint::Fill(1), Constraint::Length(1)]).split(area); 28 | Line::styled( 29 | help.keyword().unwrap_or_else(|| format!("{}.help{}", help.layer, Self::tips())), 30 | THEME.help.footer, 31 | ) 32 | .render(chunks[1], buf); 33 | 34 | Bindings::new(self.cx).render(chunks[0], buf); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /yazi-fm/src/help/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(bindings help); 2 | -------------------------------------------------------------------------------- /yazi-fm/src/input/input.rs: -------------------------------------------------------------------------------- 1 | use ratatui::{buffer::Buffer, layout::{Margin, Rect}, text::Line, widgets::{Block, BorderType, Widget}}; 2 | use yazi_config::THEME; 3 | 4 | use crate::Ctx; 5 | 6 | pub(crate) struct Input<'a> { 7 | cx: &'a Ctx, 8 | } 9 | 10 | impl<'a> Input<'a> { 11 | pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } 12 | } 13 | 14 | impl Widget for Input<'_> { 15 | fn render(self, _: Rect, buf: &mut Buffer) { 16 | let input = &self.cx.input; 17 | let area = self.cx.mgr.area(input.position); 18 | 19 | yazi_plugin::elements::Clear::default().render(area, buf); 20 | 21 | Block::bordered() 22 | .border_type(BorderType::Rounded) 23 | .border_style(THEME.input.border) 24 | .title(Line::styled(&input.title, THEME.input.title)) 25 | .render(area, buf); 26 | 27 | input.render(area.inner(Margin::new(1, 1)), buf); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /yazi-fm/src/input/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(input); 2 | -------------------------------------------------------------------------------- /yazi-fm/src/lives/filter.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use mlua::{AnyUserData, MetaMethod, UserData, UserDataMethods}; 4 | 5 | use super::{Lives, PtrCell}; 6 | 7 | pub(super) struct Filter { 8 | inner: PtrCell, 9 | } 10 | 11 | impl Deref for Filter { 12 | type Target = yazi_fs::Filter; 13 | 14 | fn deref(&self) -> &Self::Target { &self.inner } 15 | } 16 | 17 | impl Filter { 18 | #[inline] 19 | pub(super) fn make(inner: &yazi_fs::Filter) -> mlua::Result { 20 | Lives::scoped_userdata(Self { inner: inner.into() }) 21 | } 22 | } 23 | 24 | impl UserData for Filter { 25 | fn add_methods>(methods: &mut M) { 26 | methods.add_meta_method(MetaMethod::ToString, |_, me, ()| Ok(me.to_string())); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /yazi-fm/src/lives/finder.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use mlua::{AnyUserData, MetaMethod, UserData, UserDataMethods}; 4 | 5 | use super::{Lives, PtrCell}; 6 | 7 | pub(super) struct Finder { 8 | inner: PtrCell, 9 | } 10 | 11 | impl Deref for Finder { 12 | type Target = yazi_core::tab::Finder; 13 | 14 | fn deref(&self) -> &Self::Target { &self.inner } 15 | } 16 | 17 | impl Finder { 18 | #[inline] 19 | pub(super) fn make(inner: &yazi_core::tab::Finder) -> mlua::Result { 20 | Lives::scoped_userdata(Self { inner: inner.into() }) 21 | } 22 | } 23 | 24 | impl UserData for Finder { 25 | fn add_methods>(methods: &mut M) { 26 | methods.add_meta_method(MetaMethod::ToString, |_, me, ()| Ok(me.filter.to_string())); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /yazi-fm/src/lives/iter.rs: -------------------------------------------------------------------------------- 1 | use mlua::{AnyUserData, UserData}; 2 | 3 | use super::Lives; 4 | 5 | pub(super) struct Iter, T> { 6 | inner: I, 7 | count: usize, 8 | } 9 | 10 | impl + 'static, T: 'static> Iter { 11 | #[inline] 12 | pub(super) fn make(inner: I) -> mlua::Result { 13 | Lives::scoped_userdata(Self { inner, count: 0 }) 14 | } 15 | } 16 | 17 | impl, T> Iterator for Iter { 18 | type Item = (usize, T); 19 | 20 | fn next(&mut self) -> Option { 21 | let next = self.inner.next()?; 22 | self.count += 1; 23 | Some((self.count, next)) 24 | } 25 | } 26 | 27 | impl, T> UserData for Iter {} 28 | -------------------------------------------------------------------------------- /yazi-fm/src/lives/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!(context file files filter finder folder iter lives mode preference preview ptr selected tab tabs tasks yanked); 4 | -------------------------------------------------------------------------------- /yazi-fm/src/lives/mode.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use mlua::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods}; 4 | 5 | use super::{Lives, PtrCell}; 6 | 7 | pub(super) struct Mode { 8 | inner: PtrCell, 9 | } 10 | 11 | impl Deref for Mode { 12 | type Target = yazi_core::tab::Mode; 13 | 14 | fn deref(&self) -> &Self::Target { &self.inner } 15 | } 16 | 17 | impl Mode { 18 | #[inline] 19 | pub(super) fn make(inner: &yazi_core::tab::Mode) -> mlua::Result { 20 | Lives::scoped_userdata(Self { inner: inner.into() }) 21 | } 22 | } 23 | 24 | impl UserData for Mode { 25 | fn add_fields>(fields: &mut F) { 26 | fields.add_field_method_get("is_select", |_, me| Ok(me.is_select())); 27 | fields.add_field_method_get("is_unset", |_, me| Ok(me.is_unset())); 28 | fields.add_field_method_get("is_visual", |_, me| Ok(me.is_visual())); 29 | } 30 | 31 | fn add_methods>(methods: &mut M) { 32 | methods.add_meta_method(MetaMethod::ToString, |_, me, ()| Ok(me.to_string())); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /yazi-fm/src/lives/ptr.rs: -------------------------------------------------------------------------------- 1 | use std::{hash::{Hash, Hasher}, ops::Deref}; 2 | 3 | pub(super) struct PtrCell(pub(super) *const T); 4 | 5 | impl Deref for PtrCell { 6 | type Target = T; 7 | 8 | fn deref(&self) -> &Self::Target { unsafe { &*self.0 } } 9 | } 10 | 11 | impl From<&T> for PtrCell { 12 | fn from(value: &T) -> Self { Self(value) } 13 | } 14 | 15 | impl Clone for PtrCell { 16 | fn clone(&self) -> Self { *self } 17 | } 18 | 19 | impl Copy for PtrCell {} 20 | 21 | impl PartialEq for PtrCell { 22 | fn eq(&self, other: &Self) -> bool { self.0.addr() == other.0.addr() } 23 | } 24 | 25 | impl Eq for PtrCell {} 26 | 27 | impl Hash for PtrCell { 28 | fn hash(&self, state: &mut H) { state.write_usize(self.0.addr()); } 29 | } 30 | 31 | impl PtrCell { 32 | #[inline] 33 | pub(super) fn as_static(&self) -> &'static T { unsafe { &*self.0 } } 34 | } 35 | -------------------------------------------------------------------------------- /yazi-fm/src/lives/tabs.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use mlua::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods}; 4 | 5 | use super::{Lives, PtrCell, Tab}; 6 | 7 | pub(super) struct Tabs { 8 | inner: PtrCell, 9 | } 10 | 11 | impl Deref for Tabs { 12 | type Target = yazi_core::mgr::Tabs; 13 | 14 | fn deref(&self) -> &Self::Target { &self.inner } 15 | } 16 | 17 | impl Tabs { 18 | #[inline] 19 | pub(super) fn make(inner: &yazi_core::mgr::Tabs) -> mlua::Result { 20 | Lives::scoped_userdata(Self { inner: inner.into() }) 21 | } 22 | } 23 | 24 | impl UserData for Tabs { 25 | fn add_fields>(fields: &mut F) { 26 | fields.add_field_method_get("idx", |_, me| Ok(me.cursor + 1)); 27 | } 28 | 29 | fn add_methods>(methods: &mut M) { 30 | methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.len())); 31 | 32 | methods.add_meta_method(MetaMethod::Index, |_, me, idx: usize| { 33 | if idx > me.len() || idx == 0 { Ok(None) } else { Some(Tab::make(&me[idx - 1])).transpose() } 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /yazi-fm/src/lives/tasks.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields, Value}; 4 | use yazi_binding::cached_field; 5 | 6 | use super::{Lives, PtrCell}; 7 | 8 | pub(super) struct Tasks { 9 | inner: PtrCell, 10 | 11 | v_progress: Option, 12 | } 13 | 14 | impl Deref for Tasks { 15 | type Target = yazi_core::tasks::Tasks; 16 | 17 | fn deref(&self) -> &Self::Target { &self.inner } 18 | } 19 | 20 | impl Tasks { 21 | #[inline] 22 | pub(super) fn make(inner: &yazi_core::tasks::Tasks) -> mlua::Result { 23 | Lives::scoped_userdata(Self { inner: inner.into(), v_progress: None }) 24 | } 25 | } 26 | 27 | impl UserData for Tasks { 28 | fn add_fields>(fields: &mut F) { 29 | cached_field!(fields, progress, |lua, me| lua.to_value(&me.progress)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /yazi-fm/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::if_same_then_else, clippy::module_inception, clippy::unit_arg)] 2 | 3 | #[cfg(all(not(target_os = "macos"), not(target_os = "windows")))] 4 | #[global_allocator] 5 | static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; 6 | 7 | yazi_macro::mod_pub!(app cmp confirm help input lives mgr notify pick spot tasks which); 8 | 9 | yazi_macro::mod_flat!(context executor logs panic root router signals term); 10 | 11 | #[tokio::main] 12 | async fn main() -> anyhow::Result<()> { 13 | Panic::install(); 14 | yazi_shared::init(); 15 | 16 | Logs::start()?; 17 | _ = fdlimit::raise_fd_limit(); 18 | 19 | yazi_term::init(); 20 | 21 | yazi_fs::init(); 22 | 23 | yazi_config::init()?; 24 | 25 | yazi_adapter::init()?; 26 | 27 | yazi_boot::init(); 28 | 29 | yazi_proxy::init(); 30 | 31 | yazi_dds::init(); 32 | 33 | yazi_plugin::init()?; 34 | 35 | yazi_core::init(); 36 | 37 | yazi_dds::serve(); 38 | app::App::serve().await 39 | } 40 | -------------------------------------------------------------------------------- /yazi-fm/src/mgr/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(modal preview); 2 | -------------------------------------------------------------------------------- /yazi-fm/src/mgr/modal.rs: -------------------------------------------------------------------------------- 1 | use mlua::{ObjectLike, Table}; 2 | use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; 3 | use tracing::error; 4 | use yazi_plugin::{LUA, elements::render_once}; 5 | 6 | use crate::Ctx; 7 | 8 | pub(crate) struct Modal<'a> { 9 | cx: &'a Ctx, 10 | } 11 | 12 | impl<'a> Modal<'a> { 13 | #[inline] 14 | pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } 15 | } 16 | 17 | impl Widget for Modal<'_> { 18 | fn render(self, area: Rect, buf: &mut Buffer) { 19 | let mut f = || { 20 | let area = yazi_plugin::elements::Rect::from(area); 21 | let root = LUA.globals().raw_get::("Modal")?.call_method::
("new", area)?; 22 | 23 | render_once(root.call_method("children_redraw", ())?, buf, |p| self.cx.mgr.area(p)); 24 | Ok::<_, mlua::Error>(()) 25 | }; 26 | if let Err(e) = f() { 27 | error!("Failed to redraw the `Modal` component:\n{e}"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /yazi-fm/src/mgr/preview.rs: -------------------------------------------------------------------------------- 1 | use ratatui::{buffer::Buffer, widgets::Widget}; 2 | use yazi_config::LAYOUT; 3 | 4 | use crate::Ctx; 5 | 6 | pub(crate) struct Preview<'a> { 7 | cx: &'a Ctx, 8 | } 9 | 10 | impl<'a> Preview<'a> { 11 | #[inline] 12 | pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } 13 | } 14 | 15 | impl Widget for Preview<'_> { 16 | fn render(self, _: ratatui::layout::Rect, buf: &mut Buffer) { 17 | let Some(lock) = &self.cx.active().preview.lock else { 18 | return; 19 | }; 20 | 21 | if *lock.area != LAYOUT.get().preview { 22 | return; 23 | } 24 | 25 | for w in &lock.data { 26 | w.clone().render(buf, |p| self.cx.mgr.area(p)); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /yazi-fm/src/notify/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(notify); 2 | -------------------------------------------------------------------------------- /yazi-fm/src/panic.rs: -------------------------------------------------------------------------------- 1 | use crate::Term; 2 | 3 | pub(super) struct Panic; 4 | 5 | impl Panic { 6 | pub(super) fn install() { 7 | better_panic::install(); 8 | 9 | let hook = std::panic::take_hook(); 10 | std::panic::set_hook(Box::new(move |info| { 11 | Term::goodbye(|| { 12 | hook(info); 13 | 1 14 | }); 15 | })); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /yazi-fm/src/pick/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(pick); 2 | -------------------------------------------------------------------------------- /yazi-fm/src/pick/pick.rs: -------------------------------------------------------------------------------- 1 | use ratatui::{buffer::Buffer, layout::Rect, widgets::{Block, BorderType, List, ListItem, Widget}}; 2 | use yazi_config::THEME; 3 | 4 | use crate::Ctx; 5 | 6 | pub(crate) struct Pick<'a> { 7 | cx: &'a Ctx, 8 | } 9 | 10 | impl<'a> Pick<'a> { 11 | pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } 12 | } 13 | 14 | impl Widget for Pick<'_> { 15 | fn render(self, _: Rect, buf: &mut Buffer) { 16 | let pick = &self.cx.pick; 17 | let area = self.cx.mgr.area(pick.position); 18 | 19 | let items: Vec<_> = pick 20 | .window() 21 | .iter() 22 | .enumerate() 23 | .map(|(i, v)| { 24 | if i != pick.rel_cursor() { 25 | return ListItem::new(format!(" {v}")).style(THEME.pick.inactive); 26 | } 27 | 28 | ListItem::new(format!(" {v}")).style(THEME.pick.active) 29 | }) 30 | .collect(); 31 | 32 | yazi_plugin::elements::Clear::default().render(area, buf); 33 | List::new(items) 34 | .block( 35 | Block::bordered() 36 | .title(pick.title()) 37 | .border_type(BorderType::Rounded) 38 | .border_style(THEME.pick.border), 39 | ) 40 | .render(area, buf); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /yazi-fm/src/spot/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(spot); 2 | -------------------------------------------------------------------------------- /yazi-fm/src/spot/spot.rs: -------------------------------------------------------------------------------- 1 | use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; 2 | 3 | use crate::Ctx; 4 | 5 | pub(crate) struct Spot<'a> { 6 | cx: &'a Ctx, 7 | } 8 | 9 | impl<'a> Spot<'a> { 10 | pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } 11 | } 12 | 13 | impl Widget for Spot<'_> { 14 | fn render(self, _: Rect, buf: &mut Buffer) { 15 | let Some(lock) = &self.cx.active().spot.lock else { 16 | return; 17 | }; 18 | 19 | for w in &lock.data { 20 | w.clone().render(buf, |p| self.cx.mgr.area(p)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /yazi-fm/src/tasks/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(progress tasks); 2 | -------------------------------------------------------------------------------- /yazi-fm/src/tasks/progress.rs: -------------------------------------------------------------------------------- 1 | use mlua::{ObjectLike, Table}; 2 | use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; 3 | use tracing::error; 4 | use yazi_config::LAYOUT; 5 | use yazi_plugin::{LUA, elements::render_once}; 6 | 7 | use crate::Ctx; 8 | 9 | pub(crate) struct Progress<'a> { 10 | cx: &'a Ctx, 11 | } 12 | 13 | impl<'a> Progress<'a> { 14 | pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } 15 | } 16 | 17 | impl Widget for Progress<'_> { 18 | fn render(self, _: Rect, buf: &mut Buffer) { 19 | let mut f = || { 20 | let area = yazi_plugin::elements::Rect::from(LAYOUT.get().progress); 21 | let progress = 22 | LUA.globals().raw_get::
("Progress")?.call_method::
("use", area)?; 23 | 24 | render_once(progress.call_method("redraw", ())?, buf, |p| self.cx.mgr.area(p)); 25 | Ok::<_, mlua::Error>(()) 26 | }; 27 | if let Err(e) = f() { 28 | error!("Failed to redraw the `Progress` component:\n{e}"); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /yazi-fm/src/which/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(cand which); 2 | -------------------------------------------------------------------------------- /yazi-fs/src/cha/kind.rs: -------------------------------------------------------------------------------- 1 | use std::{fs::Metadata, path::Path}; 2 | 3 | use bitflags::bitflags; 4 | 5 | bitflags! { 6 | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] 7 | pub struct ChaKind: u8 { 8 | const DIR = 0b00000001; 9 | 10 | const HIDDEN = 0b00000010; 11 | const LINK = 0b00000100; 12 | const ORPHAN = 0b00001000; 13 | 14 | const DUMMY = 0b00010000; 15 | #[cfg(windows)] 16 | const SYSTEM = 0b00100000; 17 | } 18 | } 19 | 20 | impl ChaKind { 21 | #[inline] 22 | pub(super) fn hidden(_path: &Path, _meta: &Metadata) -> Self { 23 | let mut me = Self::empty(); 24 | 25 | #[cfg(unix)] 26 | if yazi_shared::url::Urn::new(_path).is_hidden() { 27 | me |= Self::HIDDEN; 28 | } 29 | #[cfg(windows)] 30 | { 31 | use std::os::windows::fs::MetadataExt; 32 | 33 | use windows_sys::Win32::Storage::FileSystem::{FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_SYSTEM}; 34 | if _meta.file_attributes() & FILE_ATTRIBUTE_HIDDEN != 0 { 35 | me |= Self::HIDDEN; 36 | } 37 | if _meta.file_attributes() & FILE_ATTRIBUTE_SYSTEM != 0 { 38 | me |= Self::SYSTEM; 39 | } 40 | } 41 | 42 | me 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /yazi-fs/src/cha/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(cha kind); 2 | -------------------------------------------------------------------------------- /yazi-fs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::if_same_then_else, clippy::option_map_unit_fn)] 2 | 3 | yazi_macro::mod_pub!(cha mounts); 4 | 5 | yazi_macro::mod_flat!(calculator cwd file files filter fns op path sorter sorting stage step xdg); 6 | 7 | pub fn init() { 8 | CWD.init(<_>::default()); 9 | 10 | mounts::init(); 11 | } 12 | -------------------------------------------------------------------------------- /yazi-fs/src/mounts/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(partition partitions); 2 | 3 | #[cfg(target_os = "linux")] 4 | yazi_macro::mod_flat!(linux); 5 | 6 | #[cfg(target_os = "macos")] 7 | yazi_macro::mod_flat!(macos); 8 | 9 | pub(super) fn init() { PARTITIONS.init(<_>::default()); } 10 | -------------------------------------------------------------------------------- /yazi-fs/src/sorting.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Display, str::FromStr}; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)] 6 | #[serde(rename_all = "kebab-case")] 7 | pub enum SortBy { 8 | #[default] 9 | None, 10 | Mtime, 11 | Btime, 12 | Extension, 13 | Alphabetical, 14 | Natural, 15 | Size, 16 | Random, 17 | } 18 | 19 | impl FromStr for SortBy { 20 | type Err = serde::de::value::Error; 21 | 22 | fn from_str(s: &str) -> Result { 23 | Self::deserialize(serde::de::value::StrDeserializer::new(s)) 24 | } 25 | } 26 | 27 | impl Display for SortBy { 28 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 29 | f.write_str(match self { 30 | Self::None => "none", 31 | Self::Mtime => "mtime", 32 | Self::Btime => "btime", 33 | Self::Extension => "extension", 34 | Self::Alphabetical => "alphabetical", 35 | Self::Natural => "natural", 36 | Self::Size => "size", 37 | Self::Random => "random", 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /yazi-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yazi-macro" 3 | version = "25.5.31" 4 | edition = "2024" 5 | license = "MIT" 6 | authors = [ "sxyazi " ] 7 | description = "Yazi macros" 8 | homepage = "https://yazi-rs.github.io" 9 | repository = "https://github.com/sxyazi/yazi" 10 | -------------------------------------------------------------------------------- /yazi-macro/src/event.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! emit { 3 | (Quit($opt:expr)) => { 4 | yazi_shared::event::Event::Quit($opt).emit(); 5 | }; 6 | (Call($cmd:expr)) => { 7 | yazi_shared::event::Event::Call(yazi_shared::event::CmdCow::from($cmd)).emit(); 8 | }; 9 | (Seq($cmds:expr)) => { 10 | yazi_shared::event::Event::Seq($cmds).emit(); 11 | }; 12 | ($event:ident) => { 13 | yazi_shared::event::Event::$event.emit(); 14 | }; 15 | } 16 | 17 | #[macro_export] 18 | macro_rules! render { 19 | () => { 20 | yazi_shared::event::NEED_RENDER.store(true, std::sync::atomic::Ordering::Relaxed); 21 | }; 22 | ($cond:expr) => { 23 | if $cond { 24 | render!(); 25 | } 26 | }; 27 | ($left:expr, > $right:expr) => {{ 28 | let val = $left; 29 | if val > $right { 30 | render!(); 31 | } 32 | val 33 | }}; 34 | } 35 | 36 | #[macro_export] 37 | macro_rules! render_and { 38 | ($cond:expr) => { 39 | if $cond { 40 | yazi_macro::render!(); 41 | true 42 | } else { 43 | false 44 | } 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /yazi-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod asset; 2 | mod event; 3 | mod log; 4 | mod module; 5 | mod platform; 6 | mod stdio; 7 | -------------------------------------------------------------------------------- /yazi-macro/src/log.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! time { 3 | ($expr:expr) => { 4 | $crate::time!(stringify!($expr), $expr) 5 | }; 6 | ($label:expr, $expr:expr) => { 7 | $crate::time!($expr, "{}", $label) 8 | }; 9 | ($expr:expr, $fmt:expr, $($args:tt)*) => {{ 10 | if tracing::enabled!(tracing::Level::DEBUG) { 11 | let start = std::time::Instant::now(); 12 | let result = $expr; 13 | tracing::debug!("{} took {:?}", format_args!($fmt, $($args)*), start.elapsed()); 14 | result 15 | } else { 16 | $expr 17 | } 18 | }}; 19 | } 20 | -------------------------------------------------------------------------------- /yazi-macro/src/module.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! mod_pub { 3 | [ $( $name:ident $(,)? )+ ] => { 4 | $( 5 | pub mod $name; 6 | )+ 7 | }; 8 | } 9 | 10 | #[macro_export] 11 | macro_rules! mod_flat { 12 | [ $( $name:ident $(,)? )+ ] => { 13 | $( 14 | mod $name; 15 | pub use $name::*; 16 | )+ 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /yazi-macro/src/platform.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! unix_either { 3 | ($a:expr, $b:expr) => {{ 4 | #[cfg(unix)] 5 | { 6 | $a 7 | } 8 | #[cfg(not(unix))] 9 | { 10 | $b 11 | } 12 | }}; 13 | } 14 | 15 | #[macro_export] 16 | macro_rules! win_either { 17 | ($a:expr, $b:expr) => {{ 18 | #[cfg(windows)] 19 | { 20 | $a 21 | } 22 | #[cfg(not(windows))] 23 | { 24 | $b 25 | } 26 | }}; 27 | } 28 | -------------------------------------------------------------------------------- /yazi-macro/src/stdio.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! outln { 3 | ($($tt:tt)*) => {{ 4 | use std::io::Write; 5 | writeln!(std::io::stdout(), $($tt)*) 6 | }} 7 | } 8 | 9 | #[macro_export] 10 | macro_rules! errln { 11 | ($($tt:tt)*) => {{ 12 | use std::io::Write; 13 | writeln!(std::io::stderr(), $($tt)*) 14 | }} 15 | } 16 | -------------------------------------------------------------------------------- /yazi-plugin/preset/compat.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -------------------------------------------------------------------------------- /yazi-plugin/preset/components/preview.lua: -------------------------------------------------------------------------------- 1 | Preview = { 2 | _id = "preview", 3 | } 4 | 5 | function Preview:new(area, tab) 6 | return setmetatable({ 7 | _area = area, 8 | _tab = tab, 9 | _folder = tab.preview.folder, 10 | }, { __index = self }) 11 | end 12 | 13 | function Preview:reflow() return { self } end 14 | 15 | function Preview:redraw() return {} end 16 | 17 | -- Mouse events 18 | function Preview:click(event, up) 19 | if up or not event.is_left then 20 | return 21 | end 22 | 23 | local y = event.y - self._area.y + 1 24 | local window = self._folder and self._folder.window or {} 25 | if window[y] then 26 | ya.emit("reveal", { window[y].url }) 27 | else 28 | ya.emit("enter", {}) 29 | end 30 | end 31 | 32 | function Preview:scroll(event, step) ya.emit("seek", { step }) end 33 | 34 | function Preview:touch(event, step) end 35 | -------------------------------------------------------------------------------- /yazi-plugin/preset/components/rail.lua: -------------------------------------------------------------------------------- 1 | Rail = { 2 | _id = "rail", 3 | } 4 | 5 | function Rail:new(chunks, tab) 6 | local me = setmetatable({ _chunks = chunks, _tab = tab }, { __index = self }) 7 | me:build() 8 | return me 9 | end 10 | 11 | function Rail:build() 12 | self._base = { 13 | ui.Bar(ui.Edge.RIGHT):area(self._chunks[1]):symbol(th.mgr.border_symbol):style(th.mgr.border_style), 14 | ui.Bar(ui.Edge.LEFT):area(self._chunks[3]):symbol(th.mgr.border_symbol):style(th.mgr.border_style), 15 | } 16 | self._children = { 17 | Marker:new(self._chunks[1], self._tab.parent), 18 | Marker:new(self._chunks[2], self._tab.current), 19 | } 20 | end 21 | 22 | function Rail:reflow() return {} end 23 | 24 | function Rail:redraw() 25 | local elements = self._base or {} 26 | for _, child in ipairs(self._children) do 27 | elements = ya.list_merge(elements, ui.redraw(child)) 28 | end 29 | return elements 30 | end 31 | 32 | -- Mouse events 33 | function Rail:click(event, up) end 34 | 35 | function Rail:scroll(event, step) end 36 | 37 | function Rail:touch(event, step) end 38 | -------------------------------------------------------------------------------- /yazi-plugin/preset/plugins/code.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | function M:peek(job) 4 | local err, bound = ya.preview_code(job) 5 | if bound then 6 | ya.emit("peek", { bound, only_if = job.file.url, upper_bound = true }) 7 | elseif err and not err:find("cancelled", 1, true) then 8 | require("empty").msg(job, err) 9 | end 10 | end 11 | 12 | function M:seek(job) 13 | local h = cx.active.current.hovered 14 | if not h or h.url ~= job.file.url then 15 | return 16 | end 17 | 18 | local step = math.floor(job.units * job.area.h / 10) 19 | step = step == 0 and ya.clamp(-1, job.units, 1) or step 20 | 21 | ya.emit("peek", { 22 | math.max(0, cx.active.preview.skip + step), 23 | only_if = job.file.url, 24 | }) 25 | end 26 | 27 | function M:spot(job) require("file"):spot(job) end 28 | 29 | return M 30 | -------------------------------------------------------------------------------- /yazi-plugin/preset/plugins/dds.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | function M:setup() 4 | ps.sub_remote("dds-emit", function(cmd) ya.emit(cmd[1], cmd[2]) end) 5 | end 6 | 7 | return M 8 | -------------------------------------------------------------------------------- /yazi-plugin/preset/plugins/noop.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | function M:peek(job) ya.preview_widget(job, {}) end 4 | 5 | function M:seek() end 6 | 7 | function M:fetch() return true end 8 | 9 | function M:preload() return true end 10 | 11 | function M:spot() end 12 | 13 | return M 14 | -------------------------------------------------------------------------------- /yazi-plugin/preset/plugins/session.lua: -------------------------------------------------------------------------------- 1 | local function setup(_, opts) 2 | if opts.sync_yanked then 3 | ps.sub_remote("@yank", function(body) ya.emit("update_yanked", { cut = body.cut, urls = body }) end) 4 | end 5 | end 6 | 7 | return { setup = setup } 8 | -------------------------------------------------------------------------------- /yazi-plugin/preset/setup.lua: -------------------------------------------------------------------------------- 1 | os.setlocale("") 2 | 3 | require("dds"):setup() 4 | require("extract"):setup() 5 | -------------------------------------------------------------------------------- /yazi-plugin/src/bindings/calculator.rs: -------------------------------------------------------------------------------- 1 | use mlua::{IntoLuaMulti, UserData, UserDataMethods, Value}; 2 | use yazi_binding::Error; 3 | 4 | pub struct SizeCalculator(pub yazi_fs::SizeCalculator); 5 | 6 | impl UserData for SizeCalculator { 7 | fn add_methods>(methods: &mut M) { 8 | methods.add_async_method_mut("recv", |lua, mut me, ()| async move { 9 | match me.0.next().await { 10 | Ok(value) => value.into_lua_multi(&lua), 11 | Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), 12 | } 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /yazi-plugin/src/bindings/icon.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use mlua::{UserData, UserDataFields, Value}; 4 | use yazi_binding::cached_field; 5 | 6 | use crate::elements::Style; 7 | 8 | pub struct Icon { 9 | inner: &'static yazi_shared::theme::Icon, 10 | 11 | v_text: Option, 12 | v_style: Option, 13 | } 14 | 15 | impl Deref for Icon { 16 | type Target = yazi_shared::theme::Icon; 17 | 18 | fn deref(&self) -> &Self::Target { self.inner } 19 | } 20 | 21 | impl From<&'static yazi_shared::theme::Icon> for Icon { 22 | fn from(icon: &'static yazi_shared::theme::Icon) -> Self { 23 | Self { inner: icon, v_text: None, v_style: None } 24 | } 25 | } 26 | 27 | impl UserData for Icon { 28 | fn add_fields>(fields: &mut F) { 29 | cached_field!(fields, text, |lua, me| lua.create_string(&me.text)); 30 | cached_field!(fields, style, |_, me| Ok(Style::from(me.style))); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /yazi-plugin/src/bindings/layer.rs: -------------------------------------------------------------------------------- 1 | use mlua::{MetaMethod, UserData}; 2 | 3 | #[derive(Clone, Copy)] 4 | pub struct Layer(yazi_shared::Layer); 5 | 6 | impl From for Layer { 7 | fn from(event: yazi_shared::Layer) -> Self { Self(event) } 8 | } 9 | 10 | impl UserData for Layer { 11 | fn add_methods>(methods: &mut M) { 12 | methods.add_meta_method(MetaMethod::ToString, |_, me, ()| Ok(me.0.to_string())); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /yazi-plugin/src/bindings/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!(calculator cha chan icon image input layer mouse permit range); 4 | -------------------------------------------------------------------------------- /yazi-plugin/src/bindings/range.rs: -------------------------------------------------------------------------------- 1 | use mlua::{IntoLua, Lua}; 2 | 3 | pub struct Range(std::ops::Range); 4 | 5 | impl From> for Range { 6 | fn from(value: std::ops::Range) -> Self { Self(value) } 7 | } 8 | 9 | impl IntoLua for Range 10 | where 11 | T: IntoLua, 12 | { 13 | fn into_lua(self, lua: &Lua) -> mlua::Result { 14 | lua.create_sequence_from([self.0.start, self.0.end])?.into_lua(lua) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /yazi-plugin/src/composer.rs: -------------------------------------------------------------------------------- 1 | use mlua::{IntoLua, Lua, MetaMethod, Table, Value}; 2 | 3 | pub struct Composer; 4 | 5 | impl Composer { 6 | pub fn make(lua: &Lua, cap: usize, f: F) -> mlua::Result 7 | where 8 | F: Fn(&Lua, &[u8]) -> mlua::Result + 'static, 9 | { 10 | let index = lua.create_function(move |lua, (ts, key): (Table, mlua::String)| { 11 | let v = f(lua, &key.as_bytes())?; 12 | ts.raw_set(key, v.clone())?; 13 | Ok(v) 14 | })?; 15 | 16 | let tbl = lua.create_table_with_capacity(0, cap)?; 17 | tbl.set_metatable(Some(lua.create_table_from([(MetaMethod::Index.name(), index)])?)); 18 | tbl.into_lua(lua) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /yazi-plugin/src/config/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(plugin runtime theme); 2 | -------------------------------------------------------------------------------- /yazi-plugin/src/elements/cell.rs: -------------------------------------------------------------------------------- 1 | use mlua::{ExternalError, FromLua}; 2 | 3 | use super::Text; 4 | 5 | const EXPECTED: &str = "expected a table of strings, Texts, Lines or Spans"; 6 | 7 | #[derive(Clone, Debug)] 8 | pub struct Cell { 9 | pub(super) text: ratatui::text::Text<'static>, 10 | } 11 | 12 | impl From for ratatui::widgets::Cell<'static> { 13 | fn from(value: Cell) -> Self { Self::new(value.text) } 14 | } 15 | 16 | impl FromLua for Cell { 17 | fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result { 18 | Ok(Self { text: Text::try_from(value).map_err(|_| EXPECTED.into_lua_err())?.inner }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /yazi-plugin/src/elements/constraint.rs: -------------------------------------------------------------------------------- 1 | use mlua::{FromLua, IntoLua, Lua, UserData, Value}; 2 | 3 | #[derive(Clone, Copy, Default, FromLua)] 4 | pub struct Constraint(pub(super) ratatui::layout::Constraint); 5 | 6 | impl Constraint { 7 | pub fn compose(lua: &Lua) -> mlua::Result { 8 | use ratatui::layout::Constraint as C; 9 | 10 | lua 11 | .create_table_from([ 12 | ("Min", lua.create_function(|_, n: u16| Ok(Self(C::Min(n))))?), 13 | ("Max", lua.create_function(|_, n: u16| Ok(Self(C::Max(n))))?), 14 | ("Length", lua.create_function(|_, n: u16| Ok(Self(C::Length(n))))?), 15 | ("Percentage", lua.create_function(|_, n: u16| Ok(Self(C::Percentage(n))))?), 16 | ("Ratio", lua.create_function(|_, (a, b): (u32, u32)| Ok(Self(C::Ratio(a, b))))?), 17 | ("Fill", lua.create_function(|_, n: u16| Ok(Self(C::Fill(n))))?), 18 | ])? 19 | .into_lua(lua) 20 | } 21 | } 22 | 23 | impl From for ratatui::layout::Constraint { 24 | fn from(value: Constraint) -> Self { value.0 } 25 | } 26 | 27 | impl UserData for Constraint {} 28 | -------------------------------------------------------------------------------- /yazi-plugin/src/elements/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!(align area bar border cell clear constraint edge elements gauge layout line list pad pos rect renderable row span style table text utils wrap); 4 | -------------------------------------------------------------------------------- /yazi-plugin/src/external/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(fd highlighter rg rga); 2 | -------------------------------------------------------------------------------- /yazi-plugin/src/file/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!(file); 4 | -------------------------------------------------------------------------------- /yazi-plugin/src/fs/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!(fs op); 4 | -------------------------------------------------------------------------------- /yazi-plugin/src/isolate/entry.rs: -------------------------------------------------------------------------------- 1 | use mlua::{ExternalResult, ObjectLike}; 2 | use tokio::runtime::Handle; 3 | use yazi_dds::Sendable; 4 | use yazi_proxy::options::PluginOpt; 5 | 6 | use super::slim_lua; 7 | use crate::loader::LOADER; 8 | 9 | pub async fn entry(opt: PluginOpt) -> mlua::Result<()> { 10 | LOADER.ensure(&opt.id, |_| ()).await.into_lua_err()?; 11 | 12 | tokio::task::spawn_blocking(move || { 13 | let lua = slim_lua(&opt.id)?; 14 | let plugin = LOADER.load_once(&lua, &opt.id)?; 15 | 16 | let job = lua.create_table_from([("args", Sendable::args_to_table(&lua, opt.args)?)])?; 17 | Handle::current().block_on(plugin.call_async_method("entry", job)) 18 | }) 19 | .await 20 | .into_lua_err()? 21 | } 22 | -------------------------------------------------------------------------------- /yazi-plugin/src/isolate/isolate.rs: -------------------------------------------------------------------------------- 1 | use mlua::Lua; 2 | use yazi_macro::plugin_preset as preset; 3 | 4 | use crate::runtime::Runtime; 5 | 6 | pub fn slim_lua(name: &str) -> mlua::Result { 7 | let lua = Lua::new(); 8 | lua.set_named_registry_value("ir", Runtime::new(name))?; 9 | 10 | // Base 11 | let globals = lua.globals(); 12 | globals.raw_set("ui", crate::elements::compose(&lua)?)?; 13 | globals.raw_set("ya", crate::utils::compose(&lua, true)?)?; 14 | globals.raw_set("fs", crate::fs::compose(&lua)?)?; 15 | globals.raw_set("rt", crate::config::Runtime::compose(&lua)?)?; 16 | globals.raw_set("th", crate::config::Theme::compose(&lua)?)?; 17 | 18 | crate::bindings::Cha::install(&lua)?; 19 | crate::file::File::install(&lua)?; 20 | yazi_binding::Url::install(&lua)?; 21 | 22 | yazi_binding::Error::install(&lua)?; 23 | crate::loader::install_isolate(&lua)?; 24 | crate::process::install(&lua)?; 25 | 26 | // Addons 27 | lua.load(preset!("ya")).set_name("ya.lua").exec()?; 28 | 29 | Ok(lua) 30 | } 31 | -------------------------------------------------------------------------------- /yazi-plugin/src/isolate/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!(entry fetch isolate peek preload seek spot); 4 | -------------------------------------------------------------------------------- /yazi-plugin/src/isolate/preload.rs: -------------------------------------------------------------------------------- 1 | use mlua::{ExternalResult, IntoLua, ObjectLike}; 2 | use tokio::runtime::Handle; 3 | use yazi_binding::Error; 4 | use yazi_config::LAYOUT; 5 | use yazi_dds::Sendable; 6 | use yazi_shared::event::Cmd; 7 | 8 | use super::slim_lua; 9 | use crate::{elements::Rect, file::File, loader::LOADER}; 10 | 11 | pub async fn preload( 12 | cmd: &'static Cmd, 13 | file: yazi_fs::File, 14 | ) -> mlua::Result<(bool, Option)> { 15 | LOADER.ensure(&cmd.name, |_| ()).await.into_lua_err()?; 16 | 17 | tokio::task::spawn_blocking(move || { 18 | let lua = slim_lua(&cmd.name)?; 19 | let plugin = LOADER.load_once(&lua, &cmd.name)?; 20 | 21 | let job = lua.create_table_from([ 22 | ("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?), 23 | ("args", Sendable::args_to_table_ref(&lua, &cmd.args)?.into_lua(&lua)?), 24 | ("file", File::new(file).into_lua(&lua)?), 25 | ("skip", 0.into_lua(&lua)?), 26 | ])?; 27 | 28 | Handle::current().block_on(plugin.call_async_method("preload", job)) 29 | }) 30 | .await 31 | .into_lua_err()? 32 | } 33 | -------------------------------------------------------------------------------- /yazi-plugin/src/isolate/seek.rs: -------------------------------------------------------------------------------- 1 | use mlua::{IntoLua, ObjectLike}; 2 | use yazi_config::LAYOUT; 3 | use yazi_proxy::{AppProxy, options::{PluginCallback, PluginOpt}}; 4 | use yazi_shared::event::Cmd; 5 | 6 | use crate::{elements::Rect, file::File}; 7 | 8 | pub fn seek_sync(cmd: &'static Cmd, file: yazi_fs::File, units: i16) { 9 | let cb: PluginCallback = Box::new(move |lua, plugin| { 10 | let job = lua.create_table_from([ 11 | ("file", File::new(file).into_lua(lua)?), 12 | ("area", Rect::from(LAYOUT.get().preview).into_lua(lua)?), 13 | ("units", units.into_lua(lua)?), 14 | ])?; 15 | 16 | plugin.call_method("seek", job) 17 | }); 18 | 19 | AppProxy::plugin(PluginOpt::new_callback(&cmd.name, cb)); 20 | } 21 | -------------------------------------------------------------------------------- /yazi-plugin/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::if_same_then_else, clippy::unit_arg)] 2 | 3 | mod macros; 4 | 5 | yazi_macro::mod_pub!(bindings config elements external file fs isolate loader process pubsub utils); 6 | 7 | yazi_macro::mod_flat!(clipboard composer lua runtime); 8 | 9 | pub fn init() -> anyhow::Result<()> { 10 | CLIPBOARD.with(<_>::default); 11 | 12 | crate::loader::init(); 13 | crate::init_lua()?; 14 | Ok(()) 15 | } 16 | -------------------------------------------------------------------------------- /yazi-plugin/src/loader/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!(chunk loader require); 4 | 5 | pub(super) fn init() { LOADER.with(<_>::default); } 6 | 7 | pub(super) fn install(lua: &mlua::Lua) -> mlua::Result<()> { Require::install(lua) } 8 | 9 | pub(super) fn install_isolate(lua: &mlua::Lua) -> mlua::Result<()> { Require::install_isolate(lua) } 10 | -------------------------------------------------------------------------------- /yazi-plugin/src/process/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!(child command output process status); 4 | -------------------------------------------------------------------------------- /yazi-plugin/src/process/output.rs: -------------------------------------------------------------------------------- 1 | use mlua::{UserData, Value}; 2 | use yazi_binding::cached_field; 3 | 4 | use super::Status; 5 | 6 | pub struct Output { 7 | inner: std::process::Output, 8 | 9 | v_status: Option, 10 | v_stdout: Option, 11 | v_stderr: Option, 12 | } 13 | 14 | impl Output { 15 | pub fn new(inner: std::process::Output) -> Self { 16 | Self { inner, v_status: None, v_stdout: None, v_stderr: None } 17 | } 18 | } 19 | 20 | impl UserData for Output { 21 | fn add_fields>(fields: &mut F) { 22 | cached_field!(fields, status, |_, me| Ok(Status::new(me.inner.status))); 23 | cached_field!(fields, stdout, |lua, me| lua.create_string(&me.inner.stdout)); 24 | cached_field!(fields, stderr, |lua, me| lua.create_string(&me.inner.stderr)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /yazi-plugin/src/process/process.rs: -------------------------------------------------------------------------------- 1 | use mlua::Lua; 2 | 3 | pub fn install(lua: &Lua) -> mlua::Result<()> { 4 | super::Command::install(lua)?; 5 | 6 | Ok(()) 7 | } 8 | -------------------------------------------------------------------------------- /yazi-plugin/src/process/status.rs: -------------------------------------------------------------------------------- 1 | use mlua::UserData; 2 | 3 | pub struct Status { 4 | inner: std::process::ExitStatus, 5 | } 6 | 7 | impl Status { 8 | pub fn new(inner: std::process::ExitStatus) -> Self { Self { inner } } 9 | } 10 | 11 | impl UserData for Status { 12 | fn add_fields>(fields: &mut F) { 13 | fields.add_field_method_get("success", |_, me| Ok(me.inner.success())); 14 | fields.add_field_method_get("code", |_, me| Ok(me.inner.code())); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /yazi-plugin/src/pubsub/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | use mlua::{IntoLua, Lua, Value}; 4 | 5 | use crate::Composer; 6 | 7 | yazi_macro::mod_flat!(pubsub); 8 | 9 | pub(super) fn compose(lua: &Lua) -> mlua::Result { 10 | Composer::make(lua, 10, |lua, key| { 11 | match key { 12 | b"pub" => Pubsub::r#pub(lua)?, 13 | b"pub_to" => Pubsub::pub_to(lua)?, 14 | b"sub" => Pubsub::sub(lua)?, 15 | b"sub_remote" => Pubsub::sub_remote(lua)?, 16 | b"unsub" => Pubsub::unsub(lua)?, 17 | b"unsub_remote" => Pubsub::unsub_remote(lua)?, 18 | _ => return Ok(Value::Nil), 19 | } 20 | .into_lua(lua) 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /yazi-plugin/src/utils/app.rs: -------------------------------------------------------------------------------- 1 | use mlua::{AnyUserData, ExternalError, Function, Lua}; 2 | use yazi_binding::Id; 3 | use yazi_proxy::{AppProxy, HIDER}; 4 | 5 | use super::Utils; 6 | use crate::bindings::{Permit, PermitRef}; 7 | 8 | impl Utils { 9 | pub(super) fn id(lua: &Lua) -> mlua::Result { 10 | lua.create_function(|_, r#type: mlua::String| { 11 | Ok(Id(match r#type.as_bytes().as_ref() { 12 | b"app" => *yazi_dds::ID, 13 | b"ft" => yazi_fs::FILES_TICKET.next(), 14 | _ => Err("Invalid id type".into_lua_err())?, 15 | })) 16 | }) 17 | } 18 | 19 | pub(super) fn hide(lua: &Lua) -> mlua::Result { 20 | lua.create_async_function(|lua, ()| async move { 21 | if lua.named_registry_value::>("HIDE_PERMIT").is_ok_and(|h| h.is_some()) { 22 | return Err("Cannot hide while already hidden".into_lua_err()); 23 | } 24 | 25 | let permit = HIDER.acquire().await.unwrap(); 26 | AppProxy::stop().await; 27 | 28 | lua.set_named_registry_value("HIDE_PERMIT", Permit::new(permit, AppProxy::resume as fn()))?; 29 | lua.named_registry_value::("HIDE_PERMIT") 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /yazi-plugin/src/utils/cache.rs: -------------------------------------------------------------------------------- 1 | use mlua::{Function, Lua, Table}; 2 | use twox_hash::XxHash3_128; 3 | use yazi_binding::Url; 4 | use yazi_config::YAZI; 5 | 6 | use super::Utils; 7 | use crate::file::FileRef; 8 | 9 | impl Utils { 10 | pub(super) fn file_cache(lua: &Lua) -> mlua::Result { 11 | lua.create_function(|_, t: Table| { 12 | let file: FileRef = t.raw_get("file")?; 13 | if file.url.parent() == Some(&YAZI.preview.cache_dir) { 14 | return Ok(None); 15 | } 16 | 17 | let hex = { 18 | let mut h = XxHash3_128::new(); 19 | h.write(file.url.as_os_str().as_encoded_bytes()); 20 | h.write(format!("//{:?}//{}", file.cha.mtime, t.raw_get("skip").unwrap_or(0)).as_bytes()); 21 | format!("{:x}", h.finish_128()) 22 | }; 23 | 24 | Ok(Some(Url::new(YAZI.preview.cache_dir.join(hex)))) 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /yazi-plugin/src/utils/call.rs: -------------------------------------------------------------------------------- 1 | use mlua::{Function, Lua, Table}; 2 | use yazi_dds::Sendable; 3 | use yazi_macro::{emit, render}; 4 | use yazi_shared::{Layer, event::Cmd}; 5 | 6 | use super::Utils; 7 | 8 | impl Utils { 9 | pub(super) fn render(lua: &Lua) -> mlua::Result { 10 | lua.create_function(|_, ()| { 11 | render!(); 12 | Ok(()) 13 | }) 14 | } 15 | 16 | pub(super) fn emit(lua: &Lua) -> mlua::Result { 17 | lua.create_function(|_, (name, args): (mlua::String, Table)| { 18 | let mut cmd = Cmd::new_or(&name.to_str()?, Layer::Mgr)?; 19 | cmd.args = Sendable::table_to_args(args)?; 20 | Ok(emit!(Call(cmd))) 21 | }) 22 | } 23 | 24 | pub(super) fn mgr_emit(lua: &Lua) -> mlua::Result { 25 | lua.create_function(|_, (name, args): (String, Table)| { 26 | emit!(Call(Cmd { name, args: Sendable::table_to_args(args)?, layer: Layer::Mgr })); 27 | Ok(()) 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /yazi-plugin/src/utils/json.rs: -------------------------------------------------------------------------------- 1 | use mlua::{Function, IntoLuaMulti, Lua, LuaSerdeExt, Value}; 2 | use yazi_binding::Error; 3 | 4 | use super::Utils; 5 | use crate::config::OPTS; 6 | 7 | impl Utils { 8 | pub(super) fn json_encode(lua: &Lua) -> mlua::Result { 9 | lua.create_async_function(|lua, value: Value| async move { 10 | match serde_json::to_string(&value) { 11 | Ok(s) => s.into_lua_multi(&lua), 12 | Err(e) => (Value::Nil, Error::Serde(e)).into_lua_multi(&lua), 13 | } 14 | }) 15 | } 16 | 17 | pub(super) fn json_decode(lua: &Lua) -> mlua::Result { 18 | lua.create_async_function(|lua, s: mlua::String| async move { 19 | match serde_json::from_slice::(&s.as_bytes()) { 20 | Ok(v) => lua.to_value_with(&v, OPTS)?.into_lua_multi(&lua), 21 | Err(e) => (Value::Nil, Error::Serde(e)).into_lua_multi(&lua), 22 | } 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /yazi-plugin/src/utils/log.rs: -------------------------------------------------------------------------------- 1 | use mlua::{Function, Lua, MultiValue}; 2 | use tracing::{debug, error}; 3 | 4 | use super::Utils; 5 | 6 | impl Utils { 7 | pub(super) fn dbg(lua: &Lua) -> mlua::Result { 8 | lua.create_function(|_, values: MultiValue| { 9 | let s = values.into_iter().map(|v| format!("{v:#?}")).collect::>().join(" "); 10 | Ok(debug!("{s}")) 11 | }) 12 | } 13 | 14 | pub(super) fn err(lua: &Lua) -> mlua::Result { 15 | lua.create_function(|_, values: MultiValue| { 16 | let s = values.into_iter().map(|v| format!("{v:#?}")).collect::>().join(" "); 17 | Ok(error!("{s}")) 18 | }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /yazi-plugin/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!( 4 | app cache call image json layer log preview process spot sync target text time user utils 5 | ); 6 | -------------------------------------------------------------------------------- /yazi-plugin/src/utils/process.rs: -------------------------------------------------------------------------------- 1 | use mlua::{Function, Lua}; 2 | 3 | use super::Utils; 4 | 5 | impl Utils { 6 | #[cfg(target_os = "macos")] 7 | pub(super) fn proc_info(lua: &Lua) -> mlua::Result { 8 | lua.create_function(|lua, pid: usize| { 9 | let info = unsafe { 10 | let mut info: libc::proc_taskinfo = std::mem::zeroed(); 11 | libc::proc_pidinfo( 12 | pid as _, 13 | libc::PROC_PIDTASKINFO, 14 | 0, 15 | &mut info as *mut _ as *mut _, 16 | std::mem::size_of_val(&info) as _, 17 | ); 18 | info 19 | }; 20 | 21 | lua.create_table_from([("mem_resident", info.pti_resident_size)]) 22 | }) 23 | } 24 | 25 | #[cfg(not(target_os = "macos"))] 26 | pub(super) fn proc_info(lua: &Lua) -> mlua::Result { 27 | lua.create_function(|lua, ()| lua.create_table()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /yazi-plugin/src/utils/target.rs: -------------------------------------------------------------------------------- 1 | use mlua::{Function, Lua}; 2 | 3 | use super::Utils; 4 | 5 | impl Utils { 6 | pub(super) fn target_os(lua: &Lua) -> mlua::Result { 7 | lua.create_function(|_, ()| Ok(std::env::consts::OS)) 8 | } 9 | 10 | pub(super) fn target_family(lua: &Lua) -> mlua::Result { 11 | lua.create_function(|_, ()| Ok(std::env::consts::FAMILY)) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /yazi-plugin/src/utils/time.rs: -------------------------------------------------------------------------------- 1 | use std::time::{SystemTime, UNIX_EPOCH}; 2 | 3 | use mlua::{ExternalError, Function, Lua}; 4 | 5 | use super::Utils; 6 | 7 | impl Utils { 8 | pub(super) fn time(lua: &Lua) -> mlua::Result { 9 | lua.create_function(|_, ()| { 10 | Ok(SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok()) 11 | }) 12 | } 13 | 14 | pub(super) fn sleep(lua: &Lua) -> mlua::Result { 15 | lua.create_async_function(|_, secs: f64| async move { 16 | if secs < 0.0 { 17 | return Err("negative sleep duration".into_lua_err()); 18 | } 19 | 20 | tokio::time::sleep(tokio::time::Duration::from_secs_f64(secs)).await; 21 | Ok(()) 22 | }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /yazi-proxy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yazi-proxy" 3 | version = "25.5.31" 4 | edition = "2024" 5 | license = "MIT" 6 | authors = [ "sxyazi " ] 7 | description = "Yazi event proxy" 8 | homepage = "https://yazi-rs.github.io" 9 | repository = "https://github.com/sxyazi/yazi" 10 | 11 | [features] 12 | default = [ "vendored-lua" ] 13 | vendored-lua = [ "mlua/vendored" ] 14 | 15 | [dependencies] 16 | yazi-config = { path = "../yazi-config", version = "25.5.31" } 17 | yazi-macro = { path = "../yazi-macro", version = "25.5.31" } 18 | yazi-shared = { path = "../yazi-shared", version = "25.5.31" } 19 | 20 | # External dependencies 21 | anyhow = { workspace = true } 22 | mlua = { workspace = true } 23 | serde = { workspace = true } 24 | tokio = { workspace = true } 25 | -------------------------------------------------------------------------------- /yazi-proxy/src/cmp.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::emit; 2 | use yazi_shared::{Id, event::Cmd}; 3 | 4 | pub struct CmpProxy; 5 | 6 | impl CmpProxy { 7 | #[inline] 8 | pub fn close() { 9 | emit!(Call(Cmd::new("cmp:close"))); 10 | } 11 | 12 | #[inline] 13 | pub fn trigger(word: &str, ticket: Id) { 14 | emit!(Call(Cmd::args("cmp:trigger", &[word]).with("ticket", ticket))); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /yazi-proxy/src/confirm.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::oneshot; 2 | use yazi_config::popup::ConfirmCfg; 3 | use yazi_macro::emit; 4 | use yazi_shared::event::Cmd; 5 | 6 | pub struct ConfirmProxy; 7 | 8 | impl ConfirmProxy { 9 | #[inline] 10 | pub async fn show(cfg: ConfirmCfg) -> bool { Self::show_rx(cfg).await.unwrap_or(false) } 11 | 12 | #[inline] 13 | pub fn show_rx(cfg: ConfirmCfg) -> oneshot::Receiver { 14 | let (tx, rx) = oneshot::channel(); 15 | emit!(Call(Cmd::new("confirm:show").with_any("tx", tx).with_any("cfg", cfg))); 16 | rx 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /yazi-proxy/src/input.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::mpsc; 2 | use yazi_config::popup::InputCfg; 3 | use yazi_macro::emit; 4 | use yazi_shared::{Id, errors::InputError, event::Cmd}; 5 | 6 | use crate::options::CmpItem; 7 | 8 | pub struct InputProxy; 9 | 10 | impl InputProxy { 11 | #[inline] 12 | pub fn show(cfg: InputCfg) -> mpsc::UnboundedReceiver> { 13 | let (tx, rx) = mpsc::unbounded_channel(); 14 | emit!(Call(Cmd::new("input:show").with_any("tx", tx).with_any("cfg", cfg))); 15 | rx 16 | } 17 | 18 | #[inline] 19 | pub fn complete(item: &CmpItem, ticket: Id) { 20 | emit!(Call(Cmd::new("input:complete").with_any("item", item.clone()).with("ticket", ticket))); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /yazi-proxy/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod macros; 2 | 3 | yazi_macro::mod_pub!(options); 4 | 5 | yazi_macro::mod_flat!(app cmp confirm input mgr pick semaphore tab tasks); 6 | 7 | pub fn init() { crate::init_semaphore(); } 8 | -------------------------------------------------------------------------------- /yazi-proxy/src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! deprecate { 3 | ($content:expr) => {{ 4 | static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); 5 | if !WARNED.swap(true, std::sync::atomic::Ordering::Relaxed) { 6 | $crate::AppProxy::notify($crate::options::NotifyOpt { 7 | title: "Deprecated API".to_owned(), 8 | content: $content.to_owned(), 9 | level: $crate::options::NotifyLevel::Warn, 10 | timeout: std::time::Duration::from_secs(20), 11 | }); 12 | } 13 | }}; 14 | } 15 | -------------------------------------------------------------------------------- /yazi-proxy/src/options/cmp.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi::OsString, path::MAIN_SEPARATOR_STR}; 2 | 3 | #[derive(Debug, Clone)] 4 | pub struct CmpItem { 5 | pub name: OsString, 6 | pub is_dir: bool, 7 | } 8 | 9 | impl CmpItem { 10 | pub fn completable(&self) -> String { 11 | format!("{}{}", self.name.to_string_lossy(), if self.is_dir { MAIN_SEPARATOR_STR } else { "" }) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /yazi-proxy/src/options/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(cmp notify open plugin process search); 2 | -------------------------------------------------------------------------------- /yazi-proxy/src/options/open.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use yazi_config::opener::OpenerRule; 4 | use yazi_shared::{event::CmdCow, url::Url}; 5 | 6 | // --- Open 7 | #[derive(Default)] 8 | pub struct OpenDoOpt { 9 | pub cwd: Url, 10 | pub hovered: Url, 11 | pub targets: Vec<(Url, &'static str)>, 12 | pub interactive: bool, 13 | } 14 | 15 | impl From for OpenDoOpt { 16 | fn from(mut c: CmdCow) -> Self { c.take_any("option").unwrap_or_default() } 17 | } 18 | 19 | // --- Open with 20 | pub struct OpenWithOpt { 21 | pub opener: Cow<'static, OpenerRule>, 22 | pub cwd: Url, 23 | pub targets: Vec, 24 | } 25 | 26 | impl TryFrom for OpenWithOpt { 27 | type Error = (); 28 | 29 | fn try_from(mut c: CmdCow) -> Result { c.take_any("option").ok_or(()) } 30 | } 31 | -------------------------------------------------------------------------------- /yazi-proxy/src/options/process.rs: -------------------------------------------------------------------------------- 1 | use std::{borrow::Cow, ffi::OsString}; 2 | 3 | use tokio::sync::oneshot; 4 | use yazi_config::opener::OpenerRule; 5 | use yazi_shared::{event::CmdCow, url::Url}; 6 | 7 | // --- Exec 8 | pub struct ProcessExecOpt { 9 | pub cwd: Url, 10 | pub opener: Cow<'static, OpenerRule>, 11 | pub args: Vec, 12 | pub done: Option>, 13 | } 14 | 15 | impl TryFrom for ProcessExecOpt { 16 | type Error = (); 17 | 18 | fn try_from(mut c: CmdCow) -> Result { c.take_any("option").ok_or(()) } 19 | } 20 | -------------------------------------------------------------------------------- /yazi-proxy/src/pick.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::oneshot; 2 | use yazi_config::popup::PickCfg; 3 | use yazi_macro::emit; 4 | use yazi_shared::event::Cmd; 5 | 6 | pub struct PickProxy; 7 | 8 | impl PickProxy { 9 | #[inline] 10 | pub async fn show(cfg: PickCfg) -> anyhow::Result { 11 | let (tx, rx) = oneshot::channel(); 12 | emit!(Call(Cmd::new("pick:show").with_any("tx", tx).with_any("cfg", cfg))); 13 | rx.await? 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /yazi-proxy/src/semaphore.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::Semaphore; 2 | use yazi_shared::RoCell; 3 | 4 | pub static HIDER: RoCell = RoCell::new(); 5 | 6 | pub static WATCHER: RoCell = RoCell::new(); 7 | 8 | pub(super) fn init_semaphore() { 9 | HIDER.init(Semaphore::new(1)); 10 | WATCHER.init(Semaphore::new(1)); 11 | } 12 | -------------------------------------------------------------------------------- /yazi-proxy/src/tab.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::emit; 2 | use yazi_shared::{event::Cmd, url::Url}; 3 | 4 | use crate::options::SearchOpt; 5 | 6 | pub struct TabProxy; 7 | 8 | impl TabProxy { 9 | #[inline] 10 | pub fn cd(target: &Url) { 11 | emit!(Call(Cmd::args("mgr:cd", &[target]))); 12 | } 13 | 14 | #[inline] 15 | pub fn reveal(target: &Url) { 16 | emit!(Call(Cmd::args("mgr:reveal", &[target]).with("no-dummy", true))); 17 | } 18 | 19 | #[inline] 20 | pub fn arrow(step: impl AsRef) { 21 | emit!(Call(Cmd::args("mgr:arrow", &[step.as_ref()]))); 22 | } 23 | 24 | #[inline] 25 | pub fn search_do(opt: SearchOpt) { 26 | emit!(Call( 27 | // TODO: use second positional argument instead of `args` parameter 28 | Cmd::args("mgr:search_do", &[opt.subject]) 29 | .with("via", opt.via.as_ref().to_owned()) 30 | .with("args", opt.args_raw.into_owned()) 31 | )); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /yazi-proxy/src/tasks.rs: -------------------------------------------------------------------------------- 1 | use std::{borrow::Cow, ffi::OsString}; 2 | 3 | use tokio::sync::oneshot; 4 | use yazi_config::opener::OpenerRule; 5 | use yazi_macro::emit; 6 | use yazi_shared::{event::Cmd, url::Url}; 7 | 8 | use crate::options::{OpenWithOpt, ProcessExecOpt}; 9 | 10 | pub struct TasksProxy; 11 | 12 | impl TasksProxy { 13 | #[inline] 14 | pub fn open_with(opener: Cow<'static, OpenerRule>, cwd: Url, targets: Vec) { 15 | emit!(Call(Cmd::new("tasks:open_with").with_any("option", OpenWithOpt { 16 | opener, 17 | cwd, 18 | targets 19 | }))); 20 | } 21 | 22 | #[inline] 23 | pub async fn process_exec(opener: Cow<'static, OpenerRule>, cwd: Url, args: Vec) { 24 | let (tx, rx) = oneshot::channel(); 25 | emit!(Call(Cmd::new("tasks:process_exec").with_any("option", ProcessExecOpt { 26 | cwd, 27 | opener, 28 | args, 29 | done: Some(tx) 30 | }))); 31 | rx.await.ok(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /yazi-scheduler/src/file/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!(file out r#in); 4 | -------------------------------------------------------------------------------- /yazi-scheduler/src/file/out.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum FileOut { 3 | Paste(FileOutPaste), 4 | Link(FileOutLink), 5 | Hardlink(FileOutHardlink), 6 | Delete(FileOutDelete), 7 | Trash(FileOutTrash), 8 | } 9 | 10 | #[derive(Debug)] 11 | pub struct FileOutPaste; 12 | 13 | #[derive(Debug)] 14 | pub struct FileOutLink; 15 | 16 | #[derive(Debug)] 17 | pub struct FileOutHardlink; 18 | 19 | #[derive(Debug)] 20 | pub struct FileOutDelete; 21 | 22 | #[derive(Debug)] 23 | pub struct FileOutTrash; 24 | -------------------------------------------------------------------------------- /yazi-scheduler/src/in.rs: -------------------------------------------------------------------------------- 1 | use crate::{file::FileIn, plugin::PluginIn, prework::PreworkIn}; 2 | 3 | #[derive(Debug)] 4 | pub enum TaskOp { 5 | File(Box), 6 | Plugin(Box), 7 | Prework(Box), 8 | } 9 | 10 | impl TaskOp { 11 | pub fn id(&self) -> usize { 12 | match self { 13 | TaskOp::File(r#in) => r#in.id(), 14 | TaskOp::Plugin(r#in) => r#in.id(), 15 | TaskOp::Prework(r#in) => r#in.id(), 16 | } 17 | } 18 | } 19 | 20 | impl From for TaskOp { 21 | fn from(r#in: FileIn) -> Self { Self::File(Box::new(r#in)) } 22 | } 23 | 24 | impl From for TaskOp { 25 | fn from(r#in: PluginIn) -> Self { Self::Plugin(Box::new(r#in)) } 26 | } 27 | 28 | impl From for TaskOp { 29 | fn from(r#in: PreworkIn) -> Self { Self::Prework(Box::new(r#in)) } 30 | } 31 | -------------------------------------------------------------------------------- /yazi-scheduler/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::option_map_unit_fn, clippy::unit_arg)] 2 | 3 | yazi_macro::mod_pub!(file plugin prework process); 4 | 5 | yazi_macro::mod_flat!(ongoing out r#in scheduler task); 6 | 7 | const LOW: u8 = yazi_config::Priority::Low as u8; 8 | const NORMAL: u8 = yazi_config::Priority::Normal as u8; 9 | const HIGH: u8 = yazi_config::Priority::High as u8; 10 | -------------------------------------------------------------------------------- /yazi-scheduler/src/out.rs: -------------------------------------------------------------------------------- 1 | use crate::{file::FileOut, plugin::PluginOut, prework::PreworkOut}; 2 | 3 | #[derive(Debug)] 4 | pub enum TaskOut { 5 | File(FileOut), 6 | Plugin(PluginOut), 7 | Prework(PreworkOut), 8 | } 9 | -------------------------------------------------------------------------------- /yazi-scheduler/src/plugin/in.rs: -------------------------------------------------------------------------------- 1 | use yazi_proxy::options::PluginOpt; 2 | 3 | #[derive(Debug)] 4 | pub enum PluginIn { 5 | Entry(PluginInEntry), 6 | } 7 | 8 | impl PluginIn { 9 | pub fn id(&self) -> usize { 10 | match self { 11 | Self::Entry(r#in) => r#in.id, 12 | } 13 | } 14 | } 15 | 16 | #[derive(Debug)] 17 | pub struct PluginInEntry { 18 | pub id: usize, 19 | pub opt: PluginOpt, 20 | } 21 | -------------------------------------------------------------------------------- /yazi-scheduler/src/plugin/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!(out plugin r#in); 4 | -------------------------------------------------------------------------------- /yazi-scheduler/src/plugin/out.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum PluginOut { 3 | Entry(PluginOutEntry), 4 | } 5 | 6 | #[derive(Debug)] 7 | pub struct PluginOutEntry; 8 | -------------------------------------------------------------------------------- /yazi-scheduler/src/prework/in.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use yazi_config::plugin::{Fetcher, Preloader}; 4 | use yazi_shared::{Throttle, url::Url}; 5 | 6 | #[derive(Debug)] 7 | pub enum PreworkIn { 8 | Fetch(PreworkInFetch), 9 | Load(PreworkInLoad), 10 | Size(PreworkInSize), 11 | } 12 | 13 | impl PreworkIn { 14 | pub fn id(&self) -> usize { 15 | match self { 16 | Self::Fetch(r#in) => r#in.id, 17 | Self::Load(r#in) => r#in.id, 18 | Self::Size(r#in) => r#in.id, 19 | } 20 | } 21 | } 22 | 23 | #[derive(Debug)] 24 | pub struct PreworkInFetch { 25 | pub id: usize, 26 | pub plugin: &'static Fetcher, 27 | pub targets: Vec, 28 | } 29 | 30 | #[derive(Clone, Debug)] 31 | pub struct PreworkInLoad { 32 | pub id: usize, 33 | pub plugin: &'static Preloader, 34 | pub target: yazi_fs::File, 35 | } 36 | 37 | #[derive(Debug)] 38 | pub struct PreworkInSize { 39 | pub id: usize, 40 | pub target: Url, 41 | pub throttle: Arc>, 42 | } 43 | -------------------------------------------------------------------------------- /yazi-scheduler/src/prework/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!(out prework r#in); 4 | -------------------------------------------------------------------------------- /yazi-scheduler/src/prework/out.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum PreworkOut { 3 | Fetch(PreworkOutFetch), 4 | Load(PreworkOutLoad), 5 | Size(PreworkOutSize), 6 | } 7 | 8 | #[derive(Debug)] 9 | pub struct PreworkOutFetch; 10 | 11 | #[derive(Debug)] 12 | pub struct PreworkOutLoad; 13 | 14 | #[derive(Debug)] 15 | pub struct PreworkOutSize; 16 | -------------------------------------------------------------------------------- /yazi-scheduler/src/process/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!(process r#in shell); 4 | -------------------------------------------------------------------------------- /yazi-shared/src/errors/input.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, fmt::{self, Display}}; 2 | 3 | use crate::Id; 4 | 5 | #[derive(Debug)] 6 | pub enum InputError { 7 | Typed(String), 8 | Completed(String, Id), 9 | Canceled(String), 10 | } 11 | 12 | impl Display for InputError { 13 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 14 | match self { 15 | Self::Typed(text) => write!(f, "Typed error: {text}"), 16 | Self::Completed(text, _) => write!(f, "Completed error: {text}"), 17 | Self::Canceled(text) => write!(f, "Canceled error: {text}"), 18 | } 19 | } 20 | } 21 | 22 | impl Error for InputError {} 23 | -------------------------------------------------------------------------------- /yazi-shared/src/errors/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(input peek); 2 | -------------------------------------------------------------------------------- /yazi-shared/src/errors/peek.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, fmt::{self, Display}}; 2 | 3 | #[derive(Debug)] 4 | pub enum PeekError { 5 | Exceed(usize), 6 | Unexpected(String), 7 | } 8 | 9 | impl Display for PeekError { 10 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 11 | match self { 12 | Self::Exceed(lines) => write!(f, "Exceed maximum lines {lines}"), 13 | Self::Unexpected(msg) => write!(f, "{msg}"), 14 | } 15 | } 16 | } 17 | 18 | impl Error for PeekError {} 19 | 20 | impl From for PeekError { 21 | fn from(error: String) -> Self { Self::Unexpected(error) } 22 | } 23 | impl From<&str> for PeekError { 24 | fn from(error: &str) -> Self { Self::from(error.to_owned()) } 25 | } 26 | impl From for PeekError { 27 | fn from(error: anyhow::Error) -> Self { Self::from(error.to_string()) } 28 | } 29 | impl From for PeekError { 30 | fn from(error: std::io::Error) -> Self { Self::from(error.to_string()) } 31 | } 32 | impl From for PeekError { 33 | fn from(error: tokio::task::JoinError) -> Self { Self::from(error.to_string()) } 34 | } 35 | -------------------------------------------------------------------------------- /yazi-shared/src/event/event.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::OsString; 2 | 3 | use crossterm::event::{KeyEvent, MouseEvent}; 4 | use tokio::sync::mpsc; 5 | 6 | use super::CmdCow; 7 | use crate::RoCell; 8 | 9 | static TX: RoCell> = RoCell::new(); 10 | static RX: RoCell> = RoCell::new(); 11 | 12 | #[derive(Debug)] 13 | pub enum Event { 14 | Call(CmdCow), 15 | Seq(Vec), 16 | Render, 17 | Key(KeyEvent), 18 | Mouse(MouseEvent), 19 | Resize, 20 | Paste(String), 21 | Quit(EventQuit), 22 | } 23 | 24 | #[derive(Debug, Default)] 25 | pub struct EventQuit { 26 | pub code: i32, 27 | pub no_cwd_file: bool, 28 | pub selected: Option, 29 | } 30 | 31 | impl Event { 32 | #[inline] 33 | pub fn init() { 34 | let (tx, rx) = mpsc::unbounded_channel(); 35 | TX.init(tx); 36 | RX.init(rx); 37 | } 38 | 39 | #[inline] 40 | pub fn take() -> mpsc::UnboundedReceiver { RX.drop() } 41 | 42 | #[inline] 43 | pub fn emit(self) { TX.send(self).ok(); } 44 | } 45 | -------------------------------------------------------------------------------- /yazi-shared/src/event/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_inception)] 2 | 3 | yazi_macro::mod_flat!(cmd cow data event); 4 | 5 | pub static NEED_RENDER: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); 6 | -------------------------------------------------------------------------------- /yazi-shared/src/layer.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Display, str::FromStr}; 2 | 3 | use serde::Deserialize; 4 | 5 | #[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy, Deserialize)] 6 | #[serde(rename_all = "kebab-case")] 7 | pub enum Layer { 8 | #[default] 9 | App, 10 | Mgr, 11 | Tasks, 12 | Spot, 13 | Pick, 14 | Input, 15 | Confirm, 16 | Help, 17 | Cmp, 18 | Which, 19 | } 20 | 21 | impl Display for Layer { 22 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 23 | f.write_str(match self { 24 | Self::App => "app", 25 | Self::Mgr => "mgr", 26 | Self::Tasks => "tasks", 27 | Self::Spot => "spot", 28 | Self::Pick => "pick", 29 | Self::Input => "input", 30 | Self::Confirm => "confirm", 31 | Self::Help => "help", 32 | Self::Cmp => "cmp", 33 | Self::Which => "which", 34 | }) 35 | } 36 | } 37 | 38 | impl FromStr for Layer { 39 | type Err = serde::de::value::Error; 40 | 41 | fn from_str(s: &str) -> Result { 42 | Self::deserialize(serde::de::value::StrDeserializer::new(s)) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /yazi-shared/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::option_map_unit_fn)] 2 | 3 | yazi_macro::mod_pub!(errors event shell theme translit url); 4 | 5 | yazi_macro::mod_flat!(chars condition debounce either env id layer natsort number os rand ro_cell sync_cell terminal throttle time utf8); 6 | 7 | pub fn init() { 8 | LOG_LEVEL.replace(<_>::from(std::env::var("YAZI_LOG").unwrap_or_default())); 9 | 10 | #[cfg(unix)] 11 | USERS_CACHE.with(<_>::default); 12 | 13 | event::Event::init(); 14 | } 15 | -------------------------------------------------------------------------------- /yazi-shared/src/number.rs: -------------------------------------------------------------------------------- 1 | use std::hash::{Hash, Hasher}; 2 | 3 | use serde::{Deserialize, Deserializer, Serialize}; 4 | 5 | #[derive(Clone, Copy, Debug, Serialize)] 6 | #[serde(transparent)] 7 | pub struct OrderedFloat(f64); 8 | 9 | impl OrderedFloat { 10 | #[inline] 11 | pub fn new(t: f64) -> Self { 12 | debug_assert!(!t.is_nan()); 13 | Self(t) 14 | } 15 | 16 | #[inline] 17 | pub const fn get(&self) -> f64 { self.0 } 18 | } 19 | 20 | impl Hash for OrderedFloat { 21 | fn hash(&self, state: &mut H) { self.0.to_bits().hash(state) } 22 | } 23 | 24 | impl PartialEq for OrderedFloat { 25 | fn eq(&self, other: &Self) -> bool { self.0.to_bits() == other.0.to_bits() } 26 | } 27 | 28 | impl Eq for OrderedFloat {} 29 | 30 | impl<'de> Deserialize<'de> for OrderedFloat { 31 | fn deserialize(deserializer: D) -> Result 32 | where 33 | D: Deserializer<'de>, 34 | { 35 | let f = f64::deserialize(deserializer)?; 36 | if f.is_nan() { 37 | Err(serde::de::Error::custom("NaN is not a valid OrderedFloat")) 38 | } else { 39 | Ok(Self::new(f)) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /yazi-shared/src/os.rs: -------------------------------------------------------------------------------- 1 | #[cfg(unix)] 2 | pub static USERS_CACHE: crate::RoCell = crate::RoCell::new(); 3 | 4 | #[cfg(unix)] 5 | pub fn hostname() -> Option<&'static str> { 6 | static CACHE: std::sync::OnceLock> = std::sync::OnceLock::new(); 7 | 8 | CACHE.get_or_init(|| hostname_impl().ok()).as_deref() 9 | } 10 | 11 | #[cfg(unix)] 12 | fn hostname_impl() -> Result { 13 | use libc::{gethostname, strlen}; 14 | 15 | let mut s = [0; 256]; 16 | let len = unsafe { 17 | if gethostname(s.as_mut_ptr() as *mut _, 255) == -1 { 18 | return Err(std::io::Error::last_os_error()); 19 | } 20 | 21 | strlen(s.as_ptr() as *const _) 22 | }; 23 | 24 | std::str::from_utf8(&s[..len]) 25 | .map_err(|_| std::io::Error::other("invalid hostname")) 26 | .map(|s| s.to_owned()) 27 | } 28 | -------------------------------------------------------------------------------- /yazi-shared/src/rand.rs: -------------------------------------------------------------------------------- 1 | use std::time::{SystemTime, UNIX_EPOCH}; 2 | 3 | pub struct LcgRng { 4 | seed: u64, 5 | } 6 | 7 | impl LcgRng { 8 | const A: u64 = 6364136223846793005; 9 | const C: u64 = 1; 10 | const M: u64 = u64::MAX; 11 | } 12 | 13 | impl Iterator for LcgRng { 14 | type Item = u64; 15 | 16 | fn next(&mut self) -> Option { 17 | self.seed = Self::A.wrapping_mul(self.seed).wrapping_add(Self::C) % Self::M; 18 | Some(self.seed) 19 | } 20 | } 21 | 22 | impl Default for LcgRng { 23 | fn default() -> Self { 24 | let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards"); 25 | Self { seed: time.as_secs() ^ time.subsec_nanos() as u64 } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /yazi-shared/src/terminal.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use crossterm::execute; 4 | 5 | #[inline] 6 | pub fn terminal_clear(mut w: impl Write) -> std::io::Result<()> { 7 | execute!( 8 | w, 9 | crossterm::terminal::Clear(crossterm::terminal::ClearType::All), 10 | crossterm::style::Print("\n") 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /yazi-shared/src/theme/color.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use anyhow::{Result, anyhow}; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Clone, Copy, Debug, Deserialize)] 7 | #[serde(try_from = "String")] 8 | pub struct Color(ratatui::style::Color); 9 | 10 | impl FromStr for Color { 11 | type Err = anyhow::Error; 12 | 13 | fn from_str(s: &str) -> Result { 14 | ratatui::style::Color::from_str(s).map(Self).map_err(|_| anyhow!("invalid color: {s}")) 15 | } 16 | } 17 | 18 | impl TryFrom for Color { 19 | type Error = anyhow::Error; 20 | 21 | fn try_from(s: String) -> Result { Self::from_str(&s) } 22 | } 23 | 24 | impl From for ratatui::style::Color { 25 | fn from(value: Color) -> Self { value.0 } 26 | } 27 | 28 | impl Serialize for Color { 29 | fn serialize(&self, serializer: S) -> Result 30 | where 31 | S: serde::Serializer, 32 | { 33 | self.0.to_string().serialize(serializer) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /yazi-shared/src/theme/icon.rs: -------------------------------------------------------------------------------- 1 | use super::Style; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct Icon { 5 | pub text: String, 6 | pub style: Style, 7 | } 8 | 9 | #[derive(Clone, Copy, Debug, Default)] 10 | pub enum IconCache { 11 | #[default] 12 | Missing, 13 | Undefined, 14 | Icon(&'static Icon), 15 | } 16 | -------------------------------------------------------------------------------- /yazi-shared/src/theme/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(color icon style); 2 | -------------------------------------------------------------------------------- /yazi-shared/src/time.rs: -------------------------------------------------------------------------------- 1 | use std::time::{SystemTime, UNIX_EPOCH}; 2 | 3 | #[inline] 4 | pub fn timestamp_us() -> u64 { 5 | SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_micros() as _ 6 | } 7 | -------------------------------------------------------------------------------- /yazi-shared/src/translit/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(table traits); 2 | -------------------------------------------------------------------------------- /yazi-shared/src/url/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(loc url urn); 2 | -------------------------------------------------------------------------------- /yazi-term/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yazi-term" 3 | version = "25.5.31" 4 | edition = "2024" 5 | license = "MIT" 6 | authors = [ "sxyazi " ] 7 | description = "Yazi terminal extensions" 8 | homepage = "https://yazi-rs.github.io" 9 | repository = "https://github.com/sxyazi/yazi" 10 | 11 | [dependencies] 12 | yazi-macro = { path = "../yazi-macro", version = "25.5.31" } 13 | yazi-shared = { path = "../yazi-shared", version = "25.5.31" } 14 | 15 | # Logging 16 | tracing = { workspace = true } 17 | 18 | # External dependencies 19 | crossterm = { workspace = true } 20 | parking_lot = { workspace = true } 21 | 22 | [target."cfg(unix)".dependencies] 23 | libc = { workspace = true } 24 | 25 | [target.'cfg(target_os = "macos")'.dependencies] 26 | crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] } 27 | 28 | [target.'cfg(windows)'.dependencies] 29 | windows-sys = { version = "0.59.0", features = [ "Win32_Globalization", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_Console", "Win32_System_Threading", "Win32_Security" ] } 30 | -------------------------------------------------------------------------------- /yazi-term/src/if.rs: -------------------------------------------------------------------------------- 1 | pub struct If(pub bool, pub T); 2 | 3 | impl crossterm::Command for If { 4 | fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result { 5 | if self.0 { self.1.write_ansi(f) } else { Ok(()) } 6 | } 7 | 8 | #[cfg(windows)] 9 | fn execute_winapi(&self) -> std::io::Result<()> { 10 | if self.0 { self.1.execute_winapi() } else { Ok(()) } 11 | } 12 | 13 | #[cfg(windows)] 14 | fn is_ansi_code_supported(&self) -> bool { self.1.is_ansi_code_supported() } 15 | } 16 | -------------------------------------------------------------------------------- /yazi-term/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::unit_arg)] 2 | 3 | yazi_macro::mod_pub!(tty); 4 | 5 | yazi_macro::mod_flat!(cursor r#if); 6 | 7 | pub fn init() { tty::init(); } 8 | -------------------------------------------------------------------------------- /yazi-term/src/tty/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(handle tty); 2 | 3 | #[cfg(windows)] 4 | yazi_macro::mod_flat!(windows); 5 | 6 | pub static TTY: yazi_shared::RoCell = yazi_shared::RoCell::new(); 7 | 8 | pub(super) fn init() { TTY.with(<_>::default); } 9 | -------------------------------------------------------------------------------- /yazi-widgets/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yazi-widgets" 3 | version = "25.5.31" 4 | edition = "2024" 5 | license = "MIT" 6 | authors = [ "sxyazi " ] 7 | description = "Yazi user interface widgets" 8 | homepage = "https://yazi-rs.github.io" 9 | repository = "https://github.com/sxyazi/yazi" 10 | 11 | [dependencies] 12 | yazi-codegen = { path = "../yazi-codegen", version = "25.5.31" } 13 | yazi-config = { path = "../yazi-config", version = "25.5.31" } 14 | yazi-macro = { path = "../yazi-macro", version = "25.5.31" } 15 | yazi-plugin = { path = "../yazi-plugin", version = "25.5.31" } 16 | yazi-proxy = { path = "../yazi-proxy", version = "25.5.31" } 17 | yazi-shared = { path = "../yazi-shared", version = "25.5.31" } 18 | 19 | # External dependencies 20 | crossterm = { workspace = true } 21 | futures = { workspace = true } 22 | ratatui = { workspace = true } 23 | unicode-width = { workspace = true } 24 | 25 | [target.'cfg(target_os = "macos")'.dependencies] 26 | crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] } 27 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/commands/backspace.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::input::Input; 5 | 6 | struct Opt { 7 | under: bool, 8 | } 9 | 10 | impl From for Opt { 11 | fn from(c: CmdCow) -> Self { Self { under: c.bool("under") } } 12 | } 13 | impl From for Opt { 14 | fn from(under: bool) -> Self { Self { under } } 15 | } 16 | 17 | impl Input { 18 | #[yazi_codegen::command] 19 | pub fn backspace(&mut self, opt: Opt) { 20 | let snap = self.snap_mut(); 21 | if !opt.under && snap.cursor < 1 { 22 | return; 23 | } else if opt.under && snap.cursor >= snap.count() { 24 | return; 25 | } 26 | 27 | if opt.under { 28 | snap.value.remove(snap.idx(snap.cursor).unwrap()); 29 | self.r#move(0); 30 | } else { 31 | snap.value.remove(snap.idx(snap.cursor - 1).unwrap()); 32 | self.r#move(-1); 33 | } 34 | 35 | self.flush_value(); 36 | render!(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/commands/backward.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::{CharKind, event::CmdCow}; 2 | 3 | use crate::input::Input; 4 | 5 | struct Opt { 6 | far: bool, 7 | } 8 | 9 | impl From for Opt { 10 | fn from(c: CmdCow) -> Self { Self { far: c.bool("far") } } 11 | } 12 | 13 | impl Input { 14 | #[yazi_codegen::command] 15 | pub fn backward(&mut self, opt: Opt) { 16 | let snap = self.snap(); 17 | if snap.cursor == 0 { 18 | return self.r#move(0); 19 | } 20 | 21 | let idx = snap.idx(snap.cursor).unwrap_or(snap.len()); 22 | let mut it = snap.value[..idx].chars().rev().enumerate(); 23 | let mut prev = CharKind::new(it.next().unwrap().1); 24 | for (i, c) in it { 25 | let k = CharKind::new(c); 26 | if prev != CharKind::Space && prev.vary(k, opt.far) { 27 | return self.r#move(-(i as isize)); 28 | } 29 | prev = k; 30 | } 31 | 32 | if prev != CharKind::Space { 33 | self.r#move(-(snap.len() as isize)); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/commands/commands.rs: -------------------------------------------------------------------------------- 1 | use yazi_shared::event::CmdCow; 2 | 3 | use crate::input::{Input, InputMode}; 4 | 5 | impl Input { 6 | pub fn execute(&mut self, cmd: CmdCow) { 7 | macro_rules! on { 8 | ($name:ident) => { 9 | if cmd.name == stringify!($name) { 10 | return self.$name(cmd); 11 | } 12 | }; 13 | ($name:ident, $alias:literal) => { 14 | if cmd.name == $alias { 15 | return self.$name(cmd); 16 | } 17 | }; 18 | } 19 | 20 | on!(r#move, "move"); 21 | on!(backward); 22 | on!(forward); 23 | 24 | match self.mode() { 25 | InputMode::Normal => { 26 | on!(insert); 27 | on!(visual); 28 | on!(replace); 29 | 30 | on!(delete); 31 | on!(yank); 32 | on!(paste); 33 | 34 | on!(undo); 35 | on!(redo); 36 | } 37 | InputMode::Insert => { 38 | on!(visual); 39 | 40 | on!(backspace); 41 | on!(kill); 42 | 43 | on!(complete); 44 | } 45 | InputMode::Replace => {} 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/commands/delete.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::input::{Input, op::InputOp}; 5 | 6 | struct Opt { 7 | cut: bool, 8 | insert: bool, 9 | } 10 | 11 | impl From for Opt { 12 | fn from(c: CmdCow) -> Self { Self { cut: c.bool("cut"), insert: c.bool("insert") } } 13 | } 14 | 15 | impl Input { 16 | #[yazi_codegen::command] 17 | pub fn delete(&mut self, opt: Opt) { 18 | match self.snap().op { 19 | InputOp::None => { 20 | self.snap_mut().op = InputOp::Delete(opt.cut, opt.insert, self.snap().cursor); 21 | } 22 | InputOp::Select(start) => { 23 | self.snap_mut().op = InputOp::Delete(opt.cut, opt.insert, start); 24 | render!(self.handle_op(self.snap().cursor, true)); 25 | self.r#move(0); 26 | } 27 | InputOp::Delete(..) => { 28 | self.snap_mut().op = InputOp::Delete(opt.cut, opt.insert, 0); 29 | self.r#move(self.snap().len() as isize); 30 | } 31 | _ => {} 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/commands/escape.rs: -------------------------------------------------------------------------------- 1 | use crate::input::{Input, InputMode, op::InputOp}; 2 | 3 | struct Opt; 4 | 5 | impl From<()> for Opt { 6 | fn from(_: ()) -> Self { Self } 7 | } 8 | 9 | impl Input { 10 | #[yazi_codegen::command] 11 | pub fn escape(&mut self, _: Opt) { 12 | let snap = self.snap_mut(); 13 | match snap.mode { 14 | InputMode::Normal => { 15 | snap.op = InputOp::None; 16 | } 17 | InputMode::Insert => { 18 | snap.mode = InputMode::Normal; 19 | self.r#move(-1); 20 | } 21 | InputMode::Replace => { 22 | snap.mode = InputMode::Normal; 23 | } 24 | } 25 | self.snaps.tag(self.limit); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/commands/insert.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::input::{Input, InputMode, op::InputOp}; 5 | 6 | struct Opt { 7 | append: bool, 8 | } 9 | 10 | impl From for Opt { 11 | fn from(c: CmdCow) -> Self { Self { append: c.bool("append") } } 12 | } 13 | impl From for Opt { 14 | fn from(append: bool) -> Self { Self { append } } 15 | } 16 | 17 | impl Input { 18 | #[yazi_codegen::command] 19 | pub fn insert(&mut self, opt: Opt) { 20 | let snap = self.snap_mut(); 21 | if snap.mode == InputMode::Normal { 22 | snap.op = InputOp::None; 23 | snap.mode = InputMode::Insert; 24 | } else { 25 | return; 26 | } 27 | 28 | if opt.append { 29 | self.r#move(1); 30 | } 31 | 32 | render!(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/commands/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_flat!(backspace backward commands complete delete escape forward insert kill paste r#move r#type redo replace undo visual yank); 2 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/commands/paste.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_plugin::CLIPBOARD; 3 | use yazi_shared::event::CmdCow; 4 | 5 | use crate::input::{Input, op::InputOp}; 6 | 7 | struct Opt { 8 | before: bool, 9 | } 10 | 11 | impl From for Opt { 12 | fn from(c: CmdCow) -> Self { Self { before: c.bool("before") } } 13 | } 14 | 15 | impl Input { 16 | #[yazi_codegen::command] 17 | pub fn paste(&mut self, opt: Opt) { 18 | if let Some(start) = self.snap().op.start() { 19 | self.snap_mut().op = InputOp::Delete(false, false, start); 20 | self.handle_op(self.snap().cursor, true); 21 | } 22 | 23 | let s = futures::executor::block_on(CLIPBOARD.get()); 24 | if s.is_empty() { 25 | return; 26 | } 27 | 28 | self.insert(!opt.before); 29 | self.type_str(&s.to_string_lossy()); 30 | self.escape(()); 31 | render!(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/commands/redo.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::input::Input; 5 | 6 | impl Input { 7 | pub fn redo(&mut self, _: CmdCow) { 8 | render!(self.snaps.redo()); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/commands/replace.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::input::{Input, InputMode, op::InputOp}; 5 | 6 | impl Input { 7 | #[yazi_codegen::command] 8 | pub fn replace(&mut self, _: CmdCow) { 9 | let snap = self.snap_mut(); 10 | if snap.mode == InputMode::Normal { 11 | snap.op = InputOp::None; 12 | snap.mode = InputMode::Replace; 13 | render!(); 14 | } 15 | } 16 | 17 | pub fn replace_str(&mut self, s: &str) { 18 | let snap = self.snap_mut(); 19 | snap.mode = InputMode::Normal; 20 | 21 | let start = snap.idx(snap.cursor).unwrap(); 22 | let mut it = snap.value[start..].char_indices(); 23 | match (it.next(), it.next()) { 24 | (None, _) => {} 25 | (Some(_), None) => snap.value.replace_range(start..snap.len(), s), 26 | (Some(_), Some((len, _))) => snap.value.replace_range(start..start + len, s), 27 | } 28 | 29 | render!(); 30 | self.snaps.tag(self.limit).then(|| self.flush_value()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/commands/type.rs: -------------------------------------------------------------------------------- 1 | use yazi_config::keymap::Key; 2 | use yazi_macro::render; 3 | use yazi_shared::event::CmdCow; 4 | 5 | use crate::input::{Input, InputMode}; 6 | 7 | struct Opt; 8 | 9 | impl From for Opt { 10 | fn from(_: CmdCow) -> Self { Self } 11 | } 12 | 13 | impl Input { 14 | pub fn r#type(&mut self, key: &Key) -> bool { 15 | let Some(c) = key.plain() else { return false }; 16 | 17 | if self.mode() == InputMode::Insert { 18 | self.type_str(c.encode_utf8(&mut [0; 4])); 19 | return true; 20 | } else if self.mode() == InputMode::Replace { 21 | self.replace_str(c.encode_utf8(&mut [0; 4])); 22 | return true; 23 | } 24 | 25 | false 26 | } 27 | 28 | pub fn type_str(&mut self, s: &str) { 29 | let snap = self.snap_mut(); 30 | if snap.cursor < 1 { 31 | snap.value.insert_str(0, s); 32 | } else { 33 | snap.value.insert_str(snap.idx(snap.cursor).unwrap(), s); 34 | } 35 | 36 | self.r#move(s.chars().count() as isize); 37 | self.flush_value(); 38 | render!(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/commands/undo.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::input::{Input, InputMode}; 5 | 6 | impl Input { 7 | pub fn undo(&mut self, _: CmdCow) { 8 | if !self.snaps.undo() { 9 | return; 10 | } 11 | if self.snap().mode == InputMode::Insert { 12 | self.escape(()); 13 | } 14 | render!(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/commands/visual.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::input::{Input, InputMode, op::InputOp}; 5 | 6 | impl Input { 7 | pub fn visual(&mut self, _: CmdCow) { 8 | if self.snap().mode != InputMode::Normal { 9 | self.escape(()); 10 | } 11 | 12 | let snap = self.snap_mut(); 13 | if !snap.value.is_empty() { 14 | snap.op = InputOp::Select(snap.cursor); 15 | render!(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/commands/yank.rs: -------------------------------------------------------------------------------- 1 | use yazi_macro::render; 2 | use yazi_shared::event::CmdCow; 3 | 4 | use crate::input::{Input, op::InputOp}; 5 | 6 | impl Input { 7 | pub fn yank(&mut self, _: CmdCow) { 8 | match self.snap().op { 9 | InputOp::None => { 10 | self.snap_mut().op = InputOp::Yank(self.snap().cursor); 11 | } 12 | InputOp::Select(start) => { 13 | self.snap_mut().op = InputOp::Yank(start); 14 | render!(self.handle_op(self.snap().cursor, true)); 15 | self.r#move(0); 16 | } 17 | InputOp::Yank(_) => { 18 | self.snap_mut().op = InputOp::Yank(0); 19 | self.r#move(self.snap().len() as isize); 20 | } 21 | _ => {} 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/mod.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_pub!(commands); 2 | 3 | yazi_macro::mod_flat!(input mode op snap snaps widget); 4 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/mode.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] 2 | pub enum InputMode { 3 | Normal, 4 | #[default] 5 | Insert, 6 | Replace, 7 | } 8 | 9 | impl InputMode { 10 | #[inline] 11 | pub(super) fn delta(&self) -> usize { (*self != InputMode::Insert) as usize } 12 | } 13 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/op.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Range; 2 | 3 | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] 4 | pub enum InputOp { 5 | #[default] 6 | None, 7 | Select(usize), 8 | Delete(bool, bool, usize), // cut, insert, start 9 | Yank(usize), 10 | } 11 | 12 | impl InputOp { 13 | #[inline] 14 | pub(super) fn start(&self) -> Option { 15 | match self { 16 | InputOp::None => None, 17 | InputOp::Select(s) => Some(*s), 18 | InputOp::Delete(.., s) => Some(*s), 19 | InputOp::Yank(s) => Some(*s), 20 | } 21 | } 22 | 23 | #[inline] 24 | pub(super) fn range(&self, cursor: usize, include: bool) -> Option> { 25 | self 26 | .start() 27 | .map(|s| if s <= cursor { (s, cursor) } else { (cursor, s) }) 28 | .map(|(s, e)| s..e + include as usize) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /yazi-widgets/src/input/widget.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Range; 2 | 3 | use ratatui::{layout::Rect, text::Line, widgets::Widget}; 4 | use yazi_config::THEME; 5 | 6 | use super::Input; 7 | 8 | impl Widget for &Input { 9 | fn render(self, area: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) 10 | where 11 | Self: Sized, 12 | { 13 | yazi_plugin::elements::Clear::default().render(area, buf); 14 | 15 | Line::styled(self.display(), THEME.input.value).render(area, buf); 16 | 17 | if let Some(Range { start, end }) = self.selected() { 18 | let s = start.min(area.width); 19 | buf.set_style( 20 | Rect { 21 | x: area.x + s, 22 | y: area.y, 23 | width: (end - start).min(area.width - s), 24 | height: area.height.min(1), 25 | }, 26 | THEME.input.selected, 27 | ); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /yazi-widgets/src/lib.rs: -------------------------------------------------------------------------------- 1 | yazi_macro::mod_pub!(input); 2 | --------------------------------------------------------------------------------