mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
a0534c0900
268 changed files with 6562 additions and 3075 deletions
25
.github/ISSUE_TEMPLATE/bug.yml
vendored
25
.github/ISSUE_TEMPLATE/bug.yml
vendored
|
|
@ -13,6 +13,8 @@ body:
|
|||
- macOS
|
||||
- Windows
|
||||
- Windows WSL
|
||||
- FreeBSD X11
|
||||
- FreeBSD Wayland
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
|
|
@ -22,31 +24,32 @@ body:
|
|||
placeholder: "ex: kitty v0.32.2"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: tried_main
|
||||
attributes:
|
||||
label: Did you try the latest code to see if this problem got fixed?
|
||||
options:
|
||||
- Tried, but the problem still
|
||||
- Not tried, and I'll explain why below
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: debug
|
||||
attributes:
|
||||
label: "`yazi --debug` output"
|
||||
description: Please do a `yazi --debug` and paste the output here.
|
||||
description: Please run `yazi --debug` and paste the debug information here.
|
||||
value: |
|
||||
<details>
|
||||
|
||||
<!-- Paste the output between the backticks below: -->
|
||||
```sh
|
||||
##### ↓↓↓ Paste the output here: ↓↓↓ #####
|
||||
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: tried_main
|
||||
attributes:
|
||||
label: Did you try the latest nightly build to see if the problem got fixed?
|
||||
options:
|
||||
- Yes, and I updated the debug information above (`yazi --debug`) to the nightly that I tried
|
||||
- No, and I'll explain why below
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
|
|
|
|||
10
.github/workflows/check.yml
vendored
10
.github/workflows/check.yml
vendored
|
|
@ -39,3 +39,13 @@ jobs:
|
|||
|
||||
- name: Rustfmt
|
||||
run: cargo +nightly fmt --all -- --check
|
||||
|
||||
stylua:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: JohnnyMorganz/stylua-action@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: latest
|
||||
args: --color always --check .
|
||||
|
|
|
|||
5
.github/workflows/release.yml
vendored
5
.github/workflows/release.yml
vendored
|
|
@ -24,7 +24,10 @@ jobs:
|
|||
|
||||
- name: Install gcc-aarch64-linux-gnu
|
||||
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
||||
run: sudo apt-get update && sudo apt-get install -yq gcc-aarch64-linux-gnu
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -yq gcc-aarch64-linux-gnu
|
||||
echo "JEMALLOC_SYS_WITH_LG_PAGE=16" >> $GITHUB_ENV
|
||||
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/usr/bin/aarch64-linux-gnu-gcc" >> $GITHUB_ENV
|
||||
|
||||
- name: Build
|
||||
run: ./scripts/build.sh ${{ matrix.target }}
|
||||
|
|
|
|||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
|
@ -28,4 +28,4 @@ jobs:
|
|||
run: cargo build --verbose
|
||||
|
||||
- name: Test
|
||||
run: cargo test --verbose
|
||||
run: cargo test --all --verbose
|
||||
|
|
|
|||
2
.styluaignore
Normal file
2
.styluaignore
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# this file caused some issues with the build system
|
||||
yazi-plugin/preset/plugins/mime.lua
|
||||
146
CONTRIBUTING.md
Normal file
146
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
# Contributing to Yazi
|
||||
|
||||
Thank you for your interest in contributing to Yazi! We welcome contributions in the form of bug reports, feature requests, documentation improvements, and code changes.
|
||||
|
||||
This guide will help you understand how to contribute to the project.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Getting Started](#getting-started)
|
||||
2. [Project Structure](#project-structure)
|
||||
3. [Development Setup](#development-setup)
|
||||
4. [How to Contribute](#how-to-contribute)
|
||||
5. [Pull Request Process](#pull-request-process)
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before you begin, ensure you have met the following requirements:
|
||||
|
||||
- Rust installed on your machine. You can download it from [rustup.rs](https://rustup.rs).
|
||||
- Familiarity with Git and GitHub.
|
||||
|
||||
### Fork the Repository
|
||||
|
||||
1. Fork the [Yazi repository](https://github.com/sxyazi/yazi) to your GitHub account.
|
||||
2. Clone your fork to your local machine:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/<your-username>/yazi.git
|
||||
```
|
||||
|
||||
3. Set up the upstream remote:
|
||||
```sh
|
||||
git remote add upstream https://github.com/sxyazi/yazi.git
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
A brief overview of the project's structure:
|
||||
|
||||
```sh
|
||||
.
|
||||
├── assets/ # Assets like images and fonts
|
||||
├── nix/ # Nix-related configurations
|
||||
├── scripts/ # Helper scripts used by CI/CD
|
||||
├── snap/ # Snapcraft configuration
|
||||
├── yazi-adapter/ # Yazi image adapter
|
||||
├── yazi-boot/ # Yazi bootstrapper
|
||||
├── yazi-cli/ # Yazi command-line interface
|
||||
├── yazi-config/ # Yazi configuration file parser
|
||||
├── yazi-core/ # Yazi core logic
|
||||
├── yazi-dds/ # Yazi data distribution service
|
||||
├── yazi-fm/ # Yazi file manager
|
||||
├── yazi-plugin/ # Yazi plugin system
|
||||
├── yazi-proxy/ # Yazi event proxy
|
||||
├── yazi-scheduler/ # Yazi task scheduler
|
||||
├── yazi-shared/ # Yazi shared library
|
||||
├── .github/ # GitHub-specific files and workflows
|
||||
├── Cargo.toml # Rust workflow configuration
|
||||
└── README.md # Project overview
|
||||
```
|
||||
|
||||
## Development Setup
|
||||
|
||||
1. Ensure the latest stable Rust is installed:
|
||||
|
||||
```sh
|
||||
rustc --version
|
||||
cargo --version
|
||||
```
|
||||
|
||||
2. Build the project:
|
||||
|
||||
```sh
|
||||
cargo build
|
||||
```
|
||||
|
||||
3. Run the tests:
|
||||
|
||||
```sh
|
||||
cargo test
|
||||
```
|
||||
|
||||
4. Format the code (requires `rustfmt` nightly):
|
||||
|
||||
```sh
|
||||
rustup component add rustfmt --toolchain nightly
|
||||
rustfmt +nightly **/*.rs
|
||||
```
|
||||
|
||||
## How to Contribute
|
||||
|
||||
### Reporting Bugs
|
||||
|
||||
If you find a bug, please file an issue.
|
||||
|
||||
### Suggesting Features
|
||||
|
||||
If you have a feature request, please file an issue.
|
||||
|
||||
### Improving Documentation
|
||||
|
||||
Yazi's documentation placed at [yazi-rs/yazi-rs.github.io](https://github.com/yazi-rs/yazi-rs.github.io), contributions related to documentation need to be made within this repository.
|
||||
|
||||
### Submitting Code Changes
|
||||
|
||||
1. Create a new branch for your changes:
|
||||
|
||||
```sh
|
||||
git checkout -b your-branch-name
|
||||
```
|
||||
|
||||
2. Make your changes. Ensure that your code follows the project's [coding style](https://github.com/sxyazi/yazi/blob/main/rustfmt.toml) and passes all tests.
|
||||
3. Commit your changes with a descriptive commit message:
|
||||
|
||||
```sh
|
||||
git commit -m "feat: an awesome feature"
|
||||
```
|
||||
|
||||
4. Push your changes to your fork:
|
||||
```sh
|
||||
git push origin your-branch-name
|
||||
```
|
||||
|
||||
## Pull Request Process
|
||||
|
||||
1. Ensure your fork is up-to-date with the upstream repository:
|
||||
|
||||
```sh
|
||||
git fetch upstream
|
||||
git checkout main
|
||||
git merge upstream/main
|
||||
```
|
||||
|
||||
2. Rebase your feature branch onto the main branch:
|
||||
|
||||
```sh
|
||||
git checkout your-branch-name
|
||||
git rebase main
|
||||
```
|
||||
|
||||
3. Create a pull request to the `main` branch of the upstream repository. Follow the pull request template and ensure that:
|
||||
- Your code passes all tests and lints.
|
||||
- Your pull request description clearly explains the changes and why they are needed.
|
||||
4. Address any review comments. Make sure to push updates to the same branch on your fork.
|
||||
662
Cargo.lock
generated
662
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
21
LICENSE-ICONS
Normal file
21
LICENSE-ICONS
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 nvim-tree
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
15
README.md
15
README.md
|
|
@ -12,13 +12,14 @@ Yazi (means "duck") is a terminal file manager written in Rust, based on non-blo
|
|||
- 💪 **Powerful Async Task Scheduling and Management**: Provides real-time progress updates, task cancellation, and internal task priority assignment.
|
||||
- 🖼️ **Built-in Support for Multiple Image Protocols**: Also integrated with Überzug++, covering almost all terminals.
|
||||
- 🌟 **Built-in Code Highlighting and Image Decoding**: Combined with the pre-loading mechanism, greatly accelerates image and normal file loading.
|
||||
- 🔌 **Concurrent Plugin System**: UI plugins (rewriting most of the UI), functional plugins, custom previewer, and custom preloader; Just some pieces of Lua.
|
||||
- 🔌 **Concurrent Plugin System**: UI plugins (rewriting most of the UI), functional plugins, custom previewer/preloader/fetcher; Just some pieces of Lua.
|
||||
- 📡 **Data Distribution Service**: Built on a client-server architecture (no additional server process required), integrated with a Lua-based publish-subscribe model, achieving cross-instance communication and state persistence.
|
||||
- 📦 **Package Manager**: Install plugins and themes with one command, keeping them always up to date, or pin them to a specific version.
|
||||
- 🧰 Integration with fd, rg, fzf, zoxide
|
||||
- 💫 Vim-like input/select/which/notify component, auto-completion for cd paths
|
||||
- 🏷️ Multi-Tab Support, Cross-directory selection, Scrollable Preview (for videos, PDFs, archives, directories, code, etc.)
|
||||
- 🔄 Bulk Renaming, Visual Mode, File Chooser
|
||||
- 🎨 Theme System, Custom Layouts, Trash Bin, CSI u
|
||||
- 🎨 Theme System, Mouse Support, Trash Bin, Custom Layouts, CSI u
|
||||
- ... and more!
|
||||
|
||||
https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265cc7
|
||||
|
|
@ -38,20 +39,20 @@ https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265
|
|||
## Image Preview
|
||||
|
||||
| Platform | Protocol | Support |
|
||||
| ----------------- | ----------------------------------------------------------------------------------------------------- | --------------------- |
|
||||
| ----------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
||||
| kitty | [Kitty unicode placeholders](https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders) | ✅ Built-in |
|
||||
| Konsole | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adaptor/src/kitty_old.rs) | ✅ Built-in |
|
||||
| Konsole | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adapter/src/kitty_old.rs) | ✅ Built-in |
|
||||
| iTerm2 | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
|
||||
| WezTerm | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
|
||||
| Mintty (Git Bash) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
|
||||
| foot | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in |
|
||||
| Ghostty | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adaptor/src/kitty_old.rs) | ✅ Built-in |
|
||||
| Ghostty | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adapter/src/kitty_old.rs) | ✅ Built-in |
|
||||
| Black Box | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in |
|
||||
| VSCode | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
|
||||
| Tabby | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
|
||||
| Hyper | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
|
||||
| X11 / Wayland | Window system protocol | ☑️ Überzug++ required |
|
||||
| Fallback | [Chafa](https://hpjansson.org/chafa/) | ☑️ Überzug++ required |
|
||||
| X11 / Wayland | Window system protocol | ☑️ [Überzug++](https://github.com/jstkdng/ueberzugpp) required |
|
||||
| Fallback | [ASCII art (Unicode block)](https://en.wikipedia.org/wiki/ASCII_art) | ☑️ [Chafa](https://hpjansson.org/chafa/) required |
|
||||
|
||||
See https://yazi-rs.github.io/docs/image-preview for details.
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"version":"0.2","flagWords":[],"language":"en","words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime"]}
|
||||
{"flagWords":[],"language":"en","words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking"],"version":"0.2"}
|
||||
12
flake.lock
generated
12
flake.lock
generated
|
|
@ -20,11 +20,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1713805509,
|
||||
"narHash": "sha256-YgSEan4CcrjivCNO5ZNzhg7/8ViLkZ4CB/GrGBVSudo=",
|
||||
"lastModified": 1716097317,
|
||||
"narHash": "sha256-1UMrLtgzielG/Sop6gl6oTSM4pDt7rF9j9VuxhDWDlY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1e1dc66fe68972a76679644a5577828b6a7e8be4",
|
||||
"rev": "8535fb92661f37ff9f0da3007fbc942f7d134b41",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -51,11 +51,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1713924823,
|
||||
"narHash": "sha256-kOeyS3GFwgnKvzuBMmFqEAX0xwZ7Nj4/5tXuvpZ0d4U=",
|
||||
"lastModified": 1716085073,
|
||||
"narHash": "sha256-3+9gI93XxszWA2+9S2xZfws1QArPX/MC6nahOGpcMB4=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "8a2edac3ae926a2a6ce60f4595dcc4540fc8cad4",
|
||||
"rev": "cfc8776011bd83508324115d353222475e1601c0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
# Resize logo
|
||||
for RES in 16 24 32 48 64 128 256; do
|
||||
mkdir -p $out/share/icons/hicolor/"$RES"x"$RES"/apps
|
||||
convert assets/logo.png -resize "$RES"x"$RES" $out/share/icons/hicolor/"$RES"x"$RES"/apps/yazi.png
|
||||
magick assets/logo.png -resize "$RES"x"$RES" $out/share/icons/hicolor/"$RES"x"$RES"/apps/yazi.png
|
||||
done
|
||||
|
||||
mkdir -p $out/share/applications
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ set -euo pipefail
|
|||
|
||||
export ARTIFACT_NAME="yazi-$1"
|
||||
export YAZI_GEN_COMPLETIONS=1
|
||||
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/usr/bin/aarch64-linux-gnu-gcc
|
||||
|
||||
# Setup Rust toolchain
|
||||
if [[ "$1" == *-musl ]]; then
|
||||
|
|
|
|||
36
scripts/icons/generate.lua
Normal file
36
scripts/icons/generate.lua
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
local dark = require("icons-default")
|
||||
local light = require("icons-light")
|
||||
|
||||
function rearrange(by)
|
||||
local map = {}
|
||||
local source = by == "exts" and "icons_by_file_extension" or "icons_by_filename"
|
||||
for k, v in pairs(dark[source]) do
|
||||
map[k] = map[k] or {}
|
||||
map[k].icon = v.icon
|
||||
map[k].fg_dark = v.color:lower()
|
||||
end
|
||||
for k, v in pairs(light[source]) do
|
||||
map[k].fg_light = v.color:lower()
|
||||
end
|
||||
return map
|
||||
end
|
||||
|
||||
function dump(map)
|
||||
local list = {}
|
||||
for k, v in pairs(map) do
|
||||
list[#list + 1] = { name = k, text = v.icon, fg_dark = v.fg_dark, fg_light = v.fg_light }
|
||||
end
|
||||
table.sort(list, function(a, b) return a.name:lower() < b.name:lower() end)
|
||||
for _, v in ipairs(list) do
|
||||
-- stylua: ignore
|
||||
print(string.format('\t{ name = "%s", text = "%s", fg_dark = "%s", fg_light = "%s" },', v.name, v.text, v.fg_dark, v.fg_light))
|
||||
end
|
||||
end
|
||||
|
||||
print("files = [")
|
||||
dump(rearrange("files"))
|
||||
print("]")
|
||||
|
||||
print("exts = [")
|
||||
dump(rearrange("exts"))
|
||||
print("]")
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
cargo publish -p yazi-shared
|
||||
cargo publish -p yazi-config
|
||||
cargo publish -p yazi-proxy
|
||||
cargo publish -p yazi-adaptor
|
||||
cargo publish -p yazi-adapter
|
||||
cargo publish -p yazi-boot
|
||||
cargo publish -p yazi-dds
|
||||
cargo publish -p yazi-scheduler
|
||||
cargo publish -p yazi-plugin
|
||||
cargo publish -p yazi-core
|
||||
cargo publish -p yazi-fm
|
||||
cargo publish -p yazi-cli
|
||||
|
|
|
|||
6
stylua.toml
Normal file
6
stylua.toml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
indent_width = 2
|
||||
call_parentheses = "NoSingleTable"
|
||||
collapse_simple_statement = "FunctionOnly"
|
||||
|
||||
[sort_requires]
|
||||
enabled = true
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
[package]
|
||||
name = "yazi-adaptor"
|
||||
name = "yazi-adapter"
|
||||
version = "0.2.5"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
description = "Yazi image adaptor"
|
||||
description = "Yazi image adapter"
|
||||
homepage = "https://yazi-rs.github.io"
|
||||
repository = "https://github.com/sxyazi/yazi"
|
||||
|
||||
|
|
@ -13,17 +13,19 @@ yazi-config = { path = "../yazi-config", version = "0.2.5" }
|
|||
yazi-shared = { path = "../yazi-shared", version = "0.2.5" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = "1.0.82"
|
||||
ansi-to-tui = "=3.1.0"
|
||||
anyhow = "1.0.86"
|
||||
arc-swap = "1.7.1"
|
||||
base64 = "0.22.0"
|
||||
base64 = "0.22.1"
|
||||
color_quant = "1.1.0"
|
||||
crossterm = "0.27.0"
|
||||
futures = "0.3.30"
|
||||
image = "0.24.9"
|
||||
imagesize = "0.12.0"
|
||||
image = "=0.24.9"
|
||||
imagesize = "0.13.0"
|
||||
kamadak-exif = "0.5.5"
|
||||
ratatui = "=0.26.1"
|
||||
tokio = { version = "1.37.0", features = [ "full" ] }
|
||||
ratatui = "0.27.0"
|
||||
scopeguard = "1.2.0"
|
||||
tokio = { version = "1.38.0", features = [ "full" ] }
|
||||
|
||||
# Logging
|
||||
tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] }
|
||||
|
|
@ -3,13 +3,13 @@ use std::{env, fmt::Display, path::Path, sync::Arc};
|
|||
use anyhow::Result;
|
||||
use ratatui::layout::Rect;
|
||||
use tracing::warn;
|
||||
use yazi_shared::{env_exists, term::Term};
|
||||
use yazi_shared::env_exists;
|
||||
|
||||
use super::{Iterm2, Kitty, KittyOld};
|
||||
use crate::{ueberzug::Ueberzug, Emulator, Sixel, SHOWN, TMUX};
|
||||
use crate::{Chafa, Emulator, Sixel, Ueberzug, SHOWN, TMUX};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum Adaptor {
|
||||
pub enum Adapter {
|
||||
Kitty,
|
||||
KittyOld,
|
||||
Iterm2,
|
||||
|
|
@ -21,7 +21,7 @@ pub enum Adaptor {
|
|||
Chafa,
|
||||
}
|
||||
|
||||
impl Display for Adaptor {
|
||||
impl Display for Adapter {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Kitty => write!(f, "kitty"),
|
||||
|
|
@ -35,49 +35,44 @@ impl Display for Adaptor {
|
|||
}
|
||||
}
|
||||
|
||||
impl Adaptor {
|
||||
pub async fn image_show(self, path: &Path, rect: Rect) -> Result<(u32, u32)> {
|
||||
impl Adapter {
|
||||
pub async fn image_show(self, path: &Path, max: Rect) -> Result<Rect> {
|
||||
if max.is_empty() {
|
||||
return Ok(Rect::default());
|
||||
}
|
||||
|
||||
match self {
|
||||
Self::Kitty => Kitty::image_show(path, rect).await,
|
||||
Self::KittyOld => KittyOld::image_show(path, rect).await,
|
||||
Self::Iterm2 => Iterm2::image_show(path, rect).await,
|
||||
Self::Sixel => Sixel::image_show(path, rect).await,
|
||||
_ => Ueberzug::image_show(path, rect).await,
|
||||
Self::Kitty => Kitty::image_show(path, max).await,
|
||||
Self::KittyOld => KittyOld::image_show(path, max).await,
|
||||
Self::Iterm2 => Iterm2::image_show(path, max).await,
|
||||
Self::Sixel => Sixel::image_show(path, max).await,
|
||||
Self::X11 | Self::Wayland => Ueberzug::image_show(path, max).await,
|
||||
Self::Chafa => Chafa::image_show(path, max).await,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn image_hide(self) -> Result<()> {
|
||||
if let Some(rect) = SHOWN.swap(None) { self.image_erase(*rect) } else { Ok(()) }
|
||||
if let Some(area) = SHOWN.swap(None) { self.image_erase(*area) } else { Ok(()) }
|
||||
}
|
||||
|
||||
pub fn image_erase(self, rect: Rect) -> Result<()> {
|
||||
pub fn image_erase(self, area: Rect) -> Result<()> {
|
||||
match self {
|
||||
Self::Kitty => Kitty::image_erase(rect),
|
||||
Self::Iterm2 => Iterm2::image_erase(rect),
|
||||
Self::KittyOld => KittyOld::image_erase(),
|
||||
Self::Sixel => Sixel::image_erase(rect),
|
||||
_ => Ueberzug::image_erase(rect),
|
||||
Self::Kitty => Kitty::image_erase(area),
|
||||
Self::Iterm2 => Iterm2::image_erase(area),
|
||||
Self::KittyOld => KittyOld::image_erase(area),
|
||||
Self::Sixel => Sixel::image_erase(area),
|
||||
Self::X11 | Self::Wayland => Ueberzug::image_erase(area),
|
||||
Self::Chafa => Chafa::image_erase(area),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn shown_load(self) -> Option<Rect> { SHOWN.load_full().map(|r| *r) }
|
||||
|
||||
pub(super) fn start(self) { Ueberzug::start(self); }
|
||||
|
||||
#[inline]
|
||||
pub(super) fn shown_store(rect: Rect, size: (u32, u32)) {
|
||||
SHOWN.store(Some(Arc::new(
|
||||
Term::ratio()
|
||||
.map(|(r1, r2)| Rect {
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
width: (size.0 as f64 / r1).ceil() as u16,
|
||||
height: (size.1 as f64 / r2).ceil() as u16,
|
||||
})
|
||||
.unwrap_or(rect),
|
||||
)));
|
||||
}
|
||||
pub(super) fn shown_store(area: Rect) { SHOWN.store(Some(Arc::new(area))); }
|
||||
|
||||
pub(super) fn start(self) { Ueberzug::start(self); }
|
||||
|
||||
#[inline]
|
||||
pub(super) fn needs_ueberzug(self) -> bool {
|
||||
|
|
@ -85,7 +80,7 @@ impl Adaptor {
|
|||
}
|
||||
}
|
||||
|
||||
impl Adaptor {
|
||||
impl Adapter {
|
||||
pub fn matches() -> Self {
|
||||
let mut protocols = Emulator::detect().adapters();
|
||||
|
||||
|
|
@ -93,8 +88,7 @@ impl Adaptor {
|
|||
protocols.retain(|p| *p == Self::Iterm2);
|
||||
if env_exists("ZELLIJ_SESSION_NAME") {
|
||||
protocols.retain(|p| *p == Self::Sixel);
|
||||
}
|
||||
if *TMUX && protocols.len() > 1 {
|
||||
} else if *TMUX {
|
||||
protocols.retain(|p| *p != Self::KittyOld);
|
||||
}
|
||||
if let Some(p) = protocols.first() {
|
||||
|
|
@ -104,7 +98,7 @@ impl Adaptor {
|
|||
match env::var("XDG_SESSION_TYPE").unwrap_or_default().as_str() {
|
||||
"x11" => return Self::X11,
|
||||
"wayland" => return Self::Wayland,
|
||||
_ => warn!("[Adaptor] Could not identify XDG_SESSION_TYPE"),
|
||||
_ => warn!("[Adapter] Could not identify XDG_SESSION_TYPE"),
|
||||
}
|
||||
if env_exists("WAYLAND_DISPLAY") {
|
||||
return Self::Wayland;
|
||||
|
|
@ -116,7 +110,7 @@ impl Adaptor {
|
|||
return Self::KittyOld;
|
||||
}
|
||||
|
||||
warn!("[Adaptor] Falling back to chafa");
|
||||
warn!("[Adapter] Falling back to chafa");
|
||||
Self::Chafa
|
||||
}
|
||||
}
|
||||
77
yazi-adapter/src/chafa.rs
Normal file
77
yazi-adapter/src/chafa.rs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
use std::{io::Write, path::Path, process::Stdio};
|
||||
|
||||
use ansi_to_tui::IntoText;
|
||||
use anyhow::{bail, Result};
|
||||
use crossterm::{cursor::MoveTo, queue};
|
||||
use ratatui::layout::Rect;
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::{Adapter, Emulator};
|
||||
|
||||
pub(super) struct Chafa;
|
||||
|
||||
impl Chafa {
|
||||
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
|
||||
let output = Command::new("chafa")
|
||||
.args([
|
||||
"-f",
|
||||
"symbols",
|
||||
"--relative",
|
||||
"off",
|
||||
"--polite",
|
||||
"on",
|
||||
"--passthrough",
|
||||
"none",
|
||||
"--animate",
|
||||
"off",
|
||||
"--view-size",
|
||||
])
|
||||
.arg(format!("{}x{}", max.width, max.height))
|
||||
.arg(path)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::null())
|
||||
.kill_on_drop(true)
|
||||
.output()
|
||||
.await?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!("chafa failed with status: {}", output.status);
|
||||
} else if output.stdout.is_empty() {
|
||||
bail!("chafa returned no output");
|
||||
}
|
||||
|
||||
let lines: Vec<_> = output.stdout.split(|&b| b == b'\n').collect();
|
||||
let Ok(Some(first)) = lines[0].into_text().map(|mut t| t.lines.pop()) else {
|
||||
bail!("failed to parse chafa output");
|
||||
};
|
||||
|
||||
let area = Rect {
|
||||
x: max.x,
|
||||
y: max.y,
|
||||
width: first.spans.into_iter().map(|s| s.content.chars().count() as u16).sum(),
|
||||
height: lines.len() as u16,
|
||||
};
|
||||
|
||||
Adapter::Chafa.image_hide()?;
|
||||
Adapter::shown_store(area);
|
||||
Emulator::move_lock((max.x, max.y), |stderr| {
|
||||
for (i, line) in lines.into_iter().enumerate() {
|
||||
stderr.write_all(line)?;
|
||||
queue!(stderr, MoveTo(max.x, max.y + i as u16 + 1))?;
|
||||
}
|
||||
Ok(area)
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn image_erase(area: Rect) -> Result<()> {
|
||||
let s = " ".repeat(area.width as usize);
|
||||
Emulator::move_lock((0, 0), |stderr| {
|
||||
for y in area.top()..area.bottom() {
|
||||
queue!(stderr, MoveTo(area.x, y))?;
|
||||
write!(stderr, "{s}")?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
35
yazi-adapter/src/dimension.rs
Normal file
35
yazi-adapter/src/dimension.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
use std::mem;
|
||||
|
||||
use crossterm::terminal::WindowSize;
|
||||
|
||||
pub struct Dimension;
|
||||
|
||||
impl Dimension {
|
||||
pub fn available() -> WindowSize {
|
||||
let mut size = WindowSize { rows: 0, columns: 0, width: 0, height: 0 };
|
||||
if let Ok(s) = crossterm::terminal::window_size() {
|
||||
_ = mem::replace(&mut size, s);
|
||||
}
|
||||
|
||||
if size.rows == 0 || size.columns == 0 {
|
||||
if let Ok(s) = crossterm::terminal::size() {
|
||||
size.columns = s.0;
|
||||
size.rows = s.1;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Use `CSI 14 t` to get the actual size of the terminal
|
||||
// if size.width == 0 || size.height == 0 {}
|
||||
|
||||
size
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ratio() -> Option<(f64, f64)> {
|
||||
let s = Self::available();
|
||||
if s.width == 0 || s.height == 0 {
|
||||
return None;
|
||||
}
|
||||
Some((f64::from(s.width) / f64::from(s.columns), f64::from(s.height) / f64::from(s.rows)))
|
||||
}
|
||||
}
|
||||
216
yazi-adapter/src/emulator.rs
Normal file
216
yazi-adapter/src/emulator.rs
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
use std::{env, io::{stderr, LineWriter}, time::Duration};
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use crossterm::{cursor::{RestorePosition, SavePosition}, execute, style::Print, terminal::{disable_raw_mode, enable_raw_mode}};
|
||||
use scopeguard::defer;
|
||||
use tokio::{io::{AsyncReadExt, BufReader}, time::timeout};
|
||||
use tracing::{error, warn};
|
||||
use yazi_shared::env_exists;
|
||||
|
||||
use crate::{Adapter, CLOSE, ESCAPE, START, TMUX};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Emulator {
|
||||
Unknown(Vec<Adapter>),
|
||||
Kitty,
|
||||
Konsole,
|
||||
Iterm2,
|
||||
WezTerm,
|
||||
Foot,
|
||||
Ghostty,
|
||||
BlackBox,
|
||||
VSCode,
|
||||
Tabby,
|
||||
Hyper,
|
||||
Mintty,
|
||||
Neovim,
|
||||
Apple,
|
||||
Urxvt,
|
||||
}
|
||||
|
||||
impl Emulator {
|
||||
pub fn adapters(self) -> Vec<Adapter> {
|
||||
match self {
|
||||
Self::Unknown(adapters) => adapters,
|
||||
Self::Kitty => vec![Adapter::Kitty],
|
||||
Self::Konsole => vec![Adapter::KittyOld],
|
||||
Self::Iterm2 => vec![Adapter::Iterm2, Adapter::Sixel],
|
||||
Self::WezTerm => vec![Adapter::Iterm2, Adapter::Sixel],
|
||||
Self::Foot => vec![Adapter::Sixel],
|
||||
Self::Ghostty => vec![Adapter::KittyOld],
|
||||
Self::BlackBox => vec![Adapter::Sixel],
|
||||
Self::VSCode => vec![Adapter::Iterm2, Adapter::Sixel],
|
||||
Self::Tabby => vec![Adapter::Iterm2, Adapter::Sixel],
|
||||
Self::Hyper => vec![Adapter::Iterm2, Adapter::Sixel],
|
||||
Self::Mintty => vec![Adapter::Iterm2],
|
||||
Self::Neovim => vec![],
|
||||
Self::Apple => vec![],
|
||||
Self::Urxvt => vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Emulator {
|
||||
pub fn detect() -> Self {
|
||||
if env_exists("NVIM_LOG_FILE") && env_exists("NVIM") {
|
||||
return Self::Neovim;
|
||||
}
|
||||
|
||||
let vars = [
|
||||
("KITTY_WINDOW_ID", Self::Kitty),
|
||||
("KONSOLE_VERSION", Self::Konsole),
|
||||
("ITERM_SESSION_ID", Self::Iterm2),
|
||||
("WEZTERM_EXECUTABLE", Self::WezTerm),
|
||||
("GHOSTTY_RESOURCES_DIR", Self::Ghostty),
|
||||
("VSCODE_INJECTION", Self::VSCode),
|
||||
("TABBY_CONFIG_DIRECTORY", Self::Tabby),
|
||||
];
|
||||
match vars.into_iter().find(|v| env_exists(v.0)) {
|
||||
Some(var) => return var.1,
|
||||
None => warn!("[Adapter] No special environment variables detected"),
|
||||
}
|
||||
|
||||
let (term, program) = Self::via_env();
|
||||
match program.as_str() {
|
||||
"iTerm.app" => return Self::Iterm2,
|
||||
"WezTerm" => return Self::WezTerm,
|
||||
"ghostty" => return Self::Ghostty,
|
||||
"BlackBox" => return Self::BlackBox,
|
||||
"vscode" => return Self::VSCode,
|
||||
"Tabby" => return Self::Tabby,
|
||||
"Hyper" => return Self::Hyper,
|
||||
"mintty" => return Self::Mintty,
|
||||
"Apple_Terminal" => return Self::Apple,
|
||||
_ => warn!("[Adapter] Unknown TERM_PROGRAM: {program}"),
|
||||
}
|
||||
match term.as_str() {
|
||||
"xterm-kitty" => return Self::Kitty,
|
||||
"foot" => return Self::Foot,
|
||||
"foot-extra" => return Self::Foot,
|
||||
"xterm-ghostty" => return Self::Ghostty,
|
||||
"rxvt-unicode-256color" => return Self::Urxvt,
|
||||
_ => warn!("[Adapter] Unknown TERM: {term}"),
|
||||
}
|
||||
|
||||
Self::via_csi().unwrap_or(Self::Unknown(vec![]))
|
||||
}
|
||||
|
||||
pub fn via_env() -> (String, String) {
|
||||
fn tmux_env(name: &str) -> Result<String> {
|
||||
let output = std::process::Command::new("tmux").args(["show-environment", name]).output()?;
|
||||
|
||||
String::from_utf8(output.stdout)?
|
||||
.trim()
|
||||
.strip_prefix(&format!("{name}="))
|
||||
.map_or_else(|| Err(anyhow!("")), |s| Ok(s.to_string()))
|
||||
}
|
||||
|
||||
let mut term = env::var("TERM").unwrap_or_default();
|
||||
let mut program = env::var("TERM_PROGRAM").unwrap_or_default();
|
||||
|
||||
if *TMUX {
|
||||
term = tmux_env("TERM").unwrap_or(term);
|
||||
program = tmux_env("TERM_PROGRAM").unwrap_or(program);
|
||||
}
|
||||
|
||||
(term, program)
|
||||
}
|
||||
|
||||
pub fn via_csi() -> Result<Self> {
|
||||
defer! { disable_raw_mode().ok(); }
|
||||
enable_raw_mode()?;
|
||||
|
||||
execute!(
|
||||
LineWriter::new(stderr()),
|
||||
SavePosition,
|
||||
Print(format!(
|
||||
"{}[>q{}_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA{}\\{}[c{}",
|
||||
START, ESCAPE, ESCAPE, ESCAPE, CLOSE
|
||||
)),
|
||||
RestorePosition
|
||||
)?;
|
||||
|
||||
let resp = futures::executor::block_on(Self::read_until_da1())?;
|
||||
let names = [
|
||||
("kitty", Self::Kitty),
|
||||
("Konsole", Self::Konsole),
|
||||
("iTerm2", Self::Iterm2),
|
||||
("WezTerm", Self::WezTerm),
|
||||
("foot", Self::Foot),
|
||||
("ghostty", Self::Ghostty),
|
||||
];
|
||||
|
||||
for (name, emulator) in names.iter() {
|
||||
if resp.contains(name) {
|
||||
return Ok(emulator.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let mut adapters = Vec::with_capacity(2);
|
||||
if resp.contains("\x1b_Gi=31;OK") {
|
||||
adapters.push(Adapter::KittyOld);
|
||||
}
|
||||
if ["?4;", "?4c", ";4;", ";4c"].iter().any(|s| resp.contains(s)) {
|
||||
adapters.push(Adapter::Sixel);
|
||||
}
|
||||
|
||||
Ok(Self::Unknown(adapters))
|
||||
}
|
||||
|
||||
pub fn move_lock<F, T>((x, y): (u16, u16), cb: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce(&mut std::io::BufWriter<std::io::StderrLock>) -> Result<T>,
|
||||
{
|
||||
use std::{io::Write, thread, time::Duration};
|
||||
|
||||
use crossterm::{cursor::{Hide, MoveTo, RestorePosition, SavePosition, Show}, queue};
|
||||
|
||||
let mut buf = std::io::BufWriter::new(stderr().lock());
|
||||
|
||||
// I really don't want to add this,
|
||||
// But tmux and ConPTY sometimes cause the cursor position to get out of sync.
|
||||
if *TMUX || cfg!(windows) {
|
||||
execute!(buf, SavePosition, MoveTo(x, y), Show)?;
|
||||
execute!(buf, MoveTo(x, y), Show)?;
|
||||
execute!(buf, MoveTo(x, y), Show)?;
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
} else {
|
||||
queue!(buf, SavePosition, MoveTo(x, y))?;
|
||||
}
|
||||
|
||||
let result = cb(&mut buf);
|
||||
if *TMUX || cfg!(windows) {
|
||||
queue!(buf, Hide, RestorePosition)?;
|
||||
} else {
|
||||
queue!(buf, RestorePosition)?;
|
||||
}
|
||||
|
||||
buf.flush()?;
|
||||
result
|
||||
}
|
||||
|
||||
pub async fn read_until_da1() -> Result<String> {
|
||||
let read = async {
|
||||
let mut stdin = BufReader::new(tokio::io::stdin());
|
||||
let mut buf = String::with_capacity(200);
|
||||
loop {
|
||||
let mut c = [0; 1];
|
||||
if stdin.read(&mut c).await? == 0 {
|
||||
bail!("unexpected EOF");
|
||||
}
|
||||
buf.push(c[0] as char);
|
||||
if c[0] == b'c' && buf.contains("\x1b[?") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(buf)
|
||||
};
|
||||
|
||||
let timeout = timeout(Duration::from_secs(10), read).await;
|
||||
if let Err(ref e) = timeout {
|
||||
error!("read_until_da1: {e:?}");
|
||||
}
|
||||
|
||||
timeout?
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,8 @@ use exif::{In, Tag};
|
|||
use image::{codecs::jpeg::JpegEncoder, imageops::{self, FilterType}, io::Limits, DynamicImage};
|
||||
use ratatui::layout::Rect;
|
||||
use yazi_config::{PREVIEW, TASKS};
|
||||
use yazi_shared::term::Term;
|
||||
|
||||
use crate::Dimension;
|
||||
|
||||
pub struct Image;
|
||||
|
||||
|
|
@ -57,7 +58,7 @@ impl Image {
|
|||
})
|
||||
.await??;
|
||||
|
||||
let (mut w, mut h) = Self::max_size(rect);
|
||||
let (mut w, mut h) = Self::max_pixel(rect);
|
||||
if (5..=8).contains(&orientation) {
|
||||
(w, h) = (h, w);
|
||||
}
|
||||
|
|
@ -76,8 +77,8 @@ impl Image {
|
|||
.await?
|
||||
}
|
||||
|
||||
pub(super) fn max_size(rect: Rect) -> (u32, u32) {
|
||||
Term::ratio()
|
||||
pub(super) fn max_pixel(rect: Rect) -> (u32, u32) {
|
||||
Dimension::ratio()
|
||||
.map(|(r1, r2)| {
|
||||
let (w, h) = ((rect.width as f64 * r1) as u32, (rect.height as f64 * r2) as u32);
|
||||
(w.min(PREVIEW.max_width), h.min(PREVIEW.max_height))
|
||||
|
|
@ -85,6 +86,17 @@ impl Image {
|
|||
.unwrap_or((PREVIEW.max_width, PREVIEW.max_height))
|
||||
}
|
||||
|
||||
pub(super) fn pixel_area(size: (u32, u32), rect: Rect) -> Rect {
|
||||
Dimension::ratio()
|
||||
.map(|(r1, r2)| Rect {
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
width: (size.0 as f64 / r1).ceil() as u16,
|
||||
height: (size.1 as f64 / r2).ceil() as u16,
|
||||
})
|
||||
.unwrap_or(rect)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn filter() -> FilterType {
|
||||
match PREVIEW.image_filter.as_str() {
|
||||
61
yazi-adapter/src/iterm2.rs
Normal file
61
yazi-adapter/src/iterm2.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
use std::{io::Write, path::Path};
|
||||
|
||||
use anyhow::Result;
|
||||
use base64::{engine::{general_purpose::STANDARD, Config}, Engine};
|
||||
use crossterm::{cursor::MoveTo, queue};
|
||||
use image::{codecs::jpeg::JpegEncoder, DynamicImage};
|
||||
use ratatui::layout::Rect;
|
||||
|
||||
use super::image::Image;
|
||||
use crate::{adapter::Adapter, Emulator, CLOSE, START};
|
||||
|
||||
pub(super) struct Iterm2;
|
||||
|
||||
impl Iterm2 {
|
||||
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
|
||||
let img = Image::downscale(path, max).await?;
|
||||
let area = Image::pixel_area((img.width(), img.height()), max);
|
||||
let b = Self::encode(img).await?;
|
||||
|
||||
Adapter::Iterm2.image_hide()?;
|
||||
Adapter::shown_store(area);
|
||||
Emulator::move_lock((max.x, max.y), |stderr| {
|
||||
stderr.write_all(&b)?;
|
||||
Ok(area)
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn image_erase(area: Rect) -> Result<()> {
|
||||
let s = " ".repeat(area.width as usize);
|
||||
Emulator::move_lock((0, 0), |stderr| {
|
||||
for y in area.top()..area.bottom() {
|
||||
queue!(stderr, MoveTo(area.x, y))?;
|
||||
write!(stderr, "{s}")?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
async fn encode(img: DynamicImage) -> Result<Vec<u8>> {
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let mut jpg = vec![];
|
||||
JpegEncoder::new_with_quality(&mut jpg, 75).encode_image(&img)?;
|
||||
|
||||
let len = base64::encoded_len(jpg.len(), STANDARD.config().encode_padding());
|
||||
let mut buf = Vec::with_capacity(200 + len.unwrap_or(1 << 16));
|
||||
|
||||
write!(
|
||||
buf,
|
||||
"{}]1337;File=inline=1;size={};width={}px;height={}px;doNotMoveCursor=1:{}\x07{}",
|
||||
START,
|
||||
jpg.len(),
|
||||
img.width(),
|
||||
img.height(),
|
||||
STANDARD.encode(&jpg),
|
||||
CLOSE
|
||||
)?;
|
||||
Ok(buf)
|
||||
})
|
||||
.await?
|
||||
}
|
||||
}
|
||||
|
|
@ -3,12 +3,12 @@ use std::{io::Write, path::Path};
|
|||
|
||||
use anyhow::Result;
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use crossterm::{cursor::MoveTo, queue};
|
||||
use image::DynamicImage;
|
||||
use ratatui::layout::Rect;
|
||||
use yazi_shared::term::Term;
|
||||
|
||||
use super::image::Image;
|
||||
use crate::{adaptor::Adaptor, CLOSE, ESCAPE, START};
|
||||
use crate::{adapter::Adapter, Emulator, CLOSE, ESCAPE, START};
|
||||
|
||||
static DIACRITICS: [char; 297] = [
|
||||
'\u{0305}',
|
||||
|
|
@ -313,31 +313,31 @@ static DIACRITICS: [char; 297] = [
|
|||
pub(super) struct Kitty;
|
||||
|
||||
impl Kitty {
|
||||
pub(super) async fn image_show(path: &Path, rect: Rect) -> Result<(u32, u32)> {
|
||||
let img = Image::downscale(path, rect).await?;
|
||||
let size = (img.width(), img.height());
|
||||
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
|
||||
let img = Image::downscale(path, max).await?;
|
||||
let area = Image::pixel_area((img.width(), img.height()), max);
|
||||
|
||||
let b1 = Self::encode(img).await?;
|
||||
let b2 = Self::place(&rect)?;
|
||||
let b2 = Self::place(&area)?;
|
||||
|
||||
Adaptor::Kitty.image_hide()?;
|
||||
Adaptor::shown_store(rect, size);
|
||||
Term::move_lock((rect.x, rect.y), |stderr| {
|
||||
Adapter::Kitty.image_hide()?;
|
||||
Adapter::shown_store(area);
|
||||
Emulator::move_lock((area.x, area.y), |stderr| {
|
||||
stderr.write_all(&b1)?;
|
||||
stderr.write_all(&b2)?;
|
||||
Ok(size)
|
||||
Ok(area)
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn image_erase(rect: Rect) -> Result<()> {
|
||||
let s = " ".repeat(rect.width as usize);
|
||||
Term::move_lock((0, 0), |stderr| {
|
||||
for y in rect.top()..rect.bottom() {
|
||||
Term::move_to(stderr, rect.x, y)?;
|
||||
pub(super) fn image_erase(area: Rect) -> Result<()> {
|
||||
let s = " ".repeat(area.width as usize);
|
||||
Emulator::move_lock((0, 0), |stderr| {
|
||||
for y in area.top()..area.bottom() {
|
||||
queue!(stderr, MoveTo(area.x, y))?;
|
||||
write!(stderr, "{s}")?;
|
||||
}
|
||||
|
||||
write!(stderr, "{}_Gq=1,a=d,d=A{}\\{}", START, ESCAPE, CLOSE)?;
|
||||
write!(stderr, "{}_Gq=2,a=d,d=A{}\\{}", START, ESCAPE, CLOSE)?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
|
@ -351,7 +351,7 @@ impl Kitty {
|
|||
if let Some(first) = it.next() {
|
||||
write!(
|
||||
buf,
|
||||
"{}_Gq=1,a=T,i=1,C=1,U=1,f={},s={},v={},m={};{}{}\\{}",
|
||||
"{}_Gq=2,a=T,i=1,C=1,U=1,f={},s={},v={},m={};{}{}\\{}",
|
||||
START,
|
||||
format,
|
||||
size.0,
|
||||
|
|
@ -388,11 +388,11 @@ impl Kitty {
|
|||
.await?
|
||||
}
|
||||
|
||||
fn place(rect: &Rect) -> Result<Vec<u8>> {
|
||||
let mut buf = Vec::with_capacity(rect.width as usize * rect.height as usize * 3 + 50);
|
||||
for y in 0..rect.height {
|
||||
write!(buf, "\x1b[{};{}H\x1b[38;5;1m", rect.y + y + 1, rect.x + 1)?;
|
||||
for x in 0..rect.width {
|
||||
fn place(area: &Rect) -> Result<Vec<u8>> {
|
||||
let mut buf = Vec::with_capacity(area.width as usize * area.height as usize * 3 + 50);
|
||||
for y in 0..area.height {
|
||||
write!(buf, "\x1b[{};{}H\x1b[38;5;1m", area.y + y + 1, area.x + 1)?;
|
||||
for x in 0..area.width {
|
||||
write!(buf, "\u{10EEEE}")?;
|
||||
write!(buf, "{}", *DIACRITICS.get(y as usize).unwrap_or(&DIACRITICS[0]))?;
|
||||
write!(buf, "{}", *DIACRITICS.get(x as usize).unwrap_or(&DIACRITICS[0]))?;
|
||||
|
|
@ -5,31 +5,30 @@ use anyhow::Result;
|
|||
use base64::{engine::general_purpose, Engine};
|
||||
use image::DynamicImage;
|
||||
use ratatui::layout::Rect;
|
||||
use yazi_shared::term::Term;
|
||||
|
||||
use super::image::Image;
|
||||
use crate::{adaptor::Adaptor, CLOSE, ESCAPE, START};
|
||||
use crate::{adapter::Adapter, Emulator, CLOSE, ESCAPE, START};
|
||||
|
||||
pub(super) struct KittyOld;
|
||||
|
||||
impl KittyOld {
|
||||
pub(super) async fn image_show(path: &Path, rect: Rect) -> Result<(u32, u32)> {
|
||||
let img = Image::downscale(path, rect).await?;
|
||||
let size = (img.width(), img.height());
|
||||
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
|
||||
let img = Image::downscale(path, max).await?;
|
||||
let area = Image::pixel_area((img.width(), img.height()), max);
|
||||
let b = Self::encode(img).await?;
|
||||
|
||||
Adaptor::KittyOld.image_hide()?;
|
||||
Adaptor::shown_store(rect, size);
|
||||
Term::move_lock((rect.x, rect.y), |stderr| {
|
||||
Adapter::KittyOld.image_hide()?;
|
||||
Adapter::shown_store(area);
|
||||
Emulator::move_lock((area.x, area.y), |stderr| {
|
||||
stderr.write_all(&b)?;
|
||||
Ok(size)
|
||||
Ok(area)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn image_erase() -> Result<()> {
|
||||
pub(super) fn image_erase(_: Rect) -> Result<()> {
|
||||
let mut stderr = LineWriter::new(stderr());
|
||||
write!(stderr, "{}_Gq=1,a=d,d=A{}\\{}", START, ESCAPE, CLOSE)?;
|
||||
write!(stderr, "{}_Gq=2,a=d,d=A{}\\{}", START, ESCAPE, CLOSE)?;
|
||||
stderr.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -43,7 +42,7 @@ impl KittyOld {
|
|||
if let Some(first) = it.next() {
|
||||
write!(
|
||||
buf,
|
||||
"{}_Gq=1,a=T,z=-1,C=1,f={},s={},v={},m={};{}{}\\{}",
|
||||
"{}_Gq=2,a=T,z=-1,C=1,f={},s={},v={},m={};{}{}\\{}",
|
||||
START,
|
||||
format,
|
||||
size.0,
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
#![allow(clippy::unit_arg)]
|
||||
|
||||
mod adaptor;
|
||||
mod adapter;
|
||||
mod chafa;
|
||||
mod dimension;
|
||||
mod emulator;
|
||||
mod image;
|
||||
mod iterm2;
|
||||
|
|
@ -9,17 +11,20 @@ mod kitty_old;
|
|||
mod sixel;
|
||||
mod ueberzug;
|
||||
|
||||
pub use adaptor::*;
|
||||
pub use adapter::*;
|
||||
use chafa::*;
|
||||
pub use dimension::*;
|
||||
pub use emulator::*;
|
||||
use iterm2::*;
|
||||
use kitty::*;
|
||||
use kitty_old::*;
|
||||
use sixel::*;
|
||||
use ueberzug::*;
|
||||
use yazi_shared::{env_exists, RoCell};
|
||||
|
||||
pub use crate::image::*;
|
||||
|
||||
pub static ADAPTOR: RoCell<Adaptor> = RoCell::new();
|
||||
pub static ADAPTOR: RoCell<Adapter> = RoCell::new();
|
||||
|
||||
// Tmux support
|
||||
pub static TMUX: RoCell<bool> = RoCell::new();
|
||||
|
|
@ -31,22 +36,22 @@ static CLOSE: RoCell<&'static str> = RoCell::new();
|
|||
static SHOWN: RoCell<arc_swap::ArcSwapOption<ratatui::layout::Rect>> = RoCell::new();
|
||||
|
||||
pub fn init() {
|
||||
TMUX.init(env_exists("TMUX"));
|
||||
TMUX.init(env_exists("TMUX") && env_exists("TMUX_PANE"));
|
||||
START.init(if *TMUX { "\x1bPtmux;\x1b\x1b" } else { "\x1b" });
|
||||
CLOSE.init(if *TMUX { "\x1b\\" } else { "" });
|
||||
ESCAPE.init(if *TMUX { "\x1b\x1b" } else { "\x1b" });
|
||||
|
||||
SHOWN.with(Default::default);
|
||||
|
||||
ADAPTOR.init(Adaptor::matches());
|
||||
ADAPTOR.start();
|
||||
|
||||
if *TMUX {
|
||||
_ = std::process::Command::new("tmux")
|
||||
.args(["set", "-p", "allow-passthrough", "on"])
|
||||
.stdin(std::process::Stdio::null())
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::null())
|
||||
.spawn();
|
||||
.status();
|
||||
}
|
||||
|
||||
SHOWN.with(Default::default);
|
||||
|
||||
ADAPTOR.init(Adapter::matches());
|
||||
ADAPTOR.start();
|
||||
}
|
||||
|
|
@ -2,34 +2,34 @@ use std::{io::Write, path::Path};
|
|||
|
||||
use anyhow::{bail, Result};
|
||||
use color_quant::NeuQuant;
|
||||
use crossterm::{cursor::MoveTo, queue};
|
||||
use image::DynamicImage;
|
||||
use ratatui::layout::Rect;
|
||||
use yazi_config::PREVIEW;
|
||||
use yazi_shared::term::Term;
|
||||
|
||||
use crate::{adaptor::Adaptor, Image, CLOSE, ESCAPE, START};
|
||||
use crate::{adapter::Adapter, Emulator, Image, CLOSE, ESCAPE, START};
|
||||
|
||||
pub(super) struct Sixel;
|
||||
|
||||
impl Sixel {
|
||||
pub(super) async fn image_show(path: &Path, rect: Rect) -> Result<(u32, u32)> {
|
||||
let img = Image::downscale(path, rect).await?;
|
||||
let size = (img.width(), img.height());
|
||||
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
|
||||
let img = Image::downscale(path, max).await?;
|
||||
let area = Image::pixel_area((img.width(), img.height()), max);
|
||||
let b = Self::encode(img).await?;
|
||||
|
||||
Adaptor::Sixel.image_hide()?;
|
||||
Adaptor::shown_store(rect, size);
|
||||
Term::move_lock((rect.x, rect.y), |stderr| {
|
||||
Adapter::Sixel.image_hide()?;
|
||||
Adapter::shown_store(area);
|
||||
Emulator::move_lock((area.x, area.y), |stderr| {
|
||||
stderr.write_all(&b)?;
|
||||
Ok(size)
|
||||
Ok(area)
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn image_erase(rect: Rect) -> Result<()> {
|
||||
let s = " ".repeat(rect.width as usize);
|
||||
Term::move_lock((0, 0), |stderr| {
|
||||
for y in rect.top()..rect.bottom() {
|
||||
Term::move_to(stderr, rect.x, y)?;
|
||||
pub(super) fn image_erase(area: Rect) -> Result<()> {
|
||||
let s = " ".repeat(area.width as usize);
|
||||
Emulator::move_lock((0, 0), |stderr| {
|
||||
for y in area.top()..area.bottom() {
|
||||
queue!(stderr, MoveTo(area.x, y))?;
|
||||
write!(stderr, "{s}")?;
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -8,7 +8,7 @@ use tracing::{debug, warn};
|
|||
use yazi_config::PREVIEW;
|
||||
use yazi_shared::RoCell;
|
||||
|
||||
use crate::{Adaptor, Image};
|
||||
use crate::{Adapter, Dimension};
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
static DEMON: RoCell<Option<UnboundedSender<Option<(PathBuf, Rect)>>>> = RoCell::new();
|
||||
|
|
@ -16,12 +16,12 @@ static DEMON: RoCell<Option<UnboundedSender<Option<(PathBuf, Rect)>>>> = RoCell:
|
|||
pub(super) struct Ueberzug;
|
||||
|
||||
impl Ueberzug {
|
||||
pub(super) fn start(adaptor: Adaptor) {
|
||||
if !adaptor.needs_ueberzug() {
|
||||
pub(super) fn start(adapter: Adapter) {
|
||||
if !adapter.needs_ueberzug() {
|
||||
return DEMON.init(None);
|
||||
}
|
||||
|
||||
let mut child = Self::create_demon(adaptor).ok();
|
||||
let mut child = Self::create_demon(adapter).ok();
|
||||
let (tx, mut rx) = mpsc::unbounded_channel();
|
||||
|
||||
tokio::spawn(async move {
|
||||
|
|
@ -31,7 +31,7 @@ impl Ueberzug {
|
|||
child = None;
|
||||
}
|
||||
if child.is_none() {
|
||||
child = Self::create_demon(adaptor).ok();
|
||||
child = Self::create_demon(adapter).ok();
|
||||
}
|
||||
if let Some(c) = &mut child {
|
||||
Self::send_command(c, cmd).await.ok();
|
||||
|
|
@ -41,25 +41,27 @@ impl Ueberzug {
|
|||
DEMON.init(Some(tx))
|
||||
}
|
||||
|
||||
pub(super) async fn image_show(path: &Path, rect: Rect) -> Result<(u32, u32)> {
|
||||
if let Some(tx) = &*DEMON {
|
||||
tx.send(Some((path.to_path_buf(), rect)))?;
|
||||
Adaptor::shown_store(rect, (0, 0));
|
||||
} else {
|
||||
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
|
||||
let Some(tx) = &*DEMON else {
|
||||
bail!("uninitialized ueberzugpp");
|
||||
}
|
||||
};
|
||||
|
||||
let path = path.to_owned();
|
||||
let p = path.to_owned();
|
||||
let ImageSize { width: w, height: h } =
|
||||
tokio::task::spawn_blocking(move || imagesize::size(path)).await??;
|
||||
tokio::task::spawn_blocking(move || imagesize::size(p)).await??;
|
||||
|
||||
let (max_w, max_h) = Image::max_size(rect);
|
||||
if w <= max_w as usize && h <= max_h as usize {
|
||||
return Ok((w as u32, h as u32));
|
||||
}
|
||||
let area = Dimension::ratio()
|
||||
.map(|(r1, r2)| Rect {
|
||||
x: max.x,
|
||||
y: max.y,
|
||||
width: max.width.min((w.min(PREVIEW.max_width as _) as f64 / r1).ceil() as _),
|
||||
height: max.height.min((h.min(PREVIEW.max_height as _) as f64 / r2).ceil() as _),
|
||||
})
|
||||
.unwrap_or(max);
|
||||
|
||||
let ratio = f64::min(max_w as f64 / w as f64, max_h as f64 / h as f64);
|
||||
Ok(((w as f64 * ratio).round() as u32, (h as f64 * ratio).round() as u32))
|
||||
tx.send(Some((path.to_owned(), area)))?;
|
||||
Adapter::shown_store(area);
|
||||
Ok(area)
|
||||
}
|
||||
|
||||
pub(super) fn image_erase(_: Rect) -> Result<()> {
|
||||
|
|
@ -70,13 +72,14 @@ impl Ueberzug {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_demon(adaptor: Adaptor) -> Result<Child> {
|
||||
fn create_demon(adapter: Adapter) -> Result<Child> {
|
||||
// TODO: demon
|
||||
let result = Command::new("ueberzugpp")
|
||||
.args(["layer", "-so", &adaptor.to_string()])
|
||||
.args(["layer", "-so", &adapter.to_string()])
|
||||
.env("SPDLOG_LEVEL", if cfg!(debug_assertions) { "debug" } else { "" })
|
||||
.kill_on_drop(true)
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn();
|
||||
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
use std::{env, io::{stderr, LineWriter}};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use crossterm::{cursor::{RestorePosition, SavePosition}, execute, style::Print, terminal::{disable_raw_mode, enable_raw_mode}};
|
||||
use tracing::warn;
|
||||
use yazi_shared::{env_exists, term::Term};
|
||||
|
||||
use crate::{Adaptor, TMUX};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Emulator {
|
||||
Unknown(Vec<Adaptor>),
|
||||
Kitty,
|
||||
Konsole,
|
||||
Iterm2,
|
||||
WezTerm,
|
||||
Foot,
|
||||
Ghostty,
|
||||
BlackBox,
|
||||
VSCode,
|
||||
Tabby,
|
||||
Hyper,
|
||||
Mintty,
|
||||
Neovim,
|
||||
Apple,
|
||||
}
|
||||
|
||||
impl Emulator {
|
||||
pub fn adapters(self) -> Vec<Adaptor> {
|
||||
match self {
|
||||
Self::Unknown(adapters) => adapters,
|
||||
Self::Kitty => vec![Adaptor::Kitty],
|
||||
Self::Konsole => vec![Adaptor::KittyOld, Adaptor::Iterm2, Adaptor::Sixel],
|
||||
Self::Iterm2 => vec![Adaptor::Iterm2, Adaptor::Sixel],
|
||||
Self::WezTerm => vec![Adaptor::Iterm2, Adaptor::Sixel],
|
||||
Self::Foot => vec![Adaptor::Sixel],
|
||||
Self::Ghostty => vec![Adaptor::KittyOld],
|
||||
Self::BlackBox => vec![Adaptor::Sixel],
|
||||
Self::VSCode => vec![Adaptor::Iterm2, Adaptor::Sixel],
|
||||
Self::Tabby => vec![Adaptor::Iterm2, Adaptor::Sixel],
|
||||
Self::Hyper => vec![Adaptor::Iterm2, Adaptor::Sixel],
|
||||
Self::Mintty => vec![Adaptor::Iterm2],
|
||||
Self::Neovim => vec![],
|
||||
Self::Apple => vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Emulator {
|
||||
pub fn detect() -> Self {
|
||||
if env_exists("NVIM_LOG_FILE") && env_exists("NVIM") {
|
||||
return Self::Neovim;
|
||||
}
|
||||
|
||||
let vars = [
|
||||
("KITTY_WINDOW_ID", Self::Kitty),
|
||||
("KONSOLE_VERSION", Self::Konsole),
|
||||
("ITERM_SESSION_ID", Self::Iterm2),
|
||||
("WEZTERM_EXECUTABLE", Self::WezTerm),
|
||||
("GHOSTTY_RESOURCES_DIR", Self::Ghostty),
|
||||
("VSCODE_INJECTION", Self::VSCode),
|
||||
("TABBY_CONFIG_DIRECTORY", Self::Tabby),
|
||||
];
|
||||
match vars.into_iter().find(|v| env_exists(v.0)) {
|
||||
Some(var) => return var.1,
|
||||
None => warn!("[Adaptor] No special environment variables detected"),
|
||||
}
|
||||
|
||||
let (term, program) = Self::via_env();
|
||||
match program.as_str() {
|
||||
"iTerm.app" => return Self::Iterm2,
|
||||
"WezTerm" => return Self::WezTerm,
|
||||
"ghostty" => return Self::Ghostty,
|
||||
"BlackBox" => return Self::BlackBox,
|
||||
"vscode" => return Self::VSCode,
|
||||
"Tabby" => return Self::Tabby,
|
||||
"Hyper" => return Self::Hyper,
|
||||
"mintty" => return Self::Mintty,
|
||||
"Apple_Terminal" => return Self::Apple,
|
||||
_ => warn!("[Adaptor] Unknown TERM_PROGRAM: {program}"),
|
||||
}
|
||||
match term.as_str() {
|
||||
"xterm-kitty" => return Self::Kitty,
|
||||
"foot" => return Self::Foot,
|
||||
"foot-extra" => return Self::Foot,
|
||||
"xterm-ghostty" => return Self::Ghostty,
|
||||
_ => warn!("[Adaptor] Unknown TERM: {term}"),
|
||||
}
|
||||
|
||||
Self::via_csi().unwrap_or(Self::Unknown(vec![]))
|
||||
}
|
||||
|
||||
pub fn via_env() -> (String, String) {
|
||||
fn tmux_env(name: &str) -> Result<String> {
|
||||
let output = std::process::Command::new("tmux").args(["show-environment", name]).output()?;
|
||||
|
||||
String::from_utf8(output.stdout)?
|
||||
.trim()
|
||||
.strip_prefix(&format!("{name}="))
|
||||
.map_or_else(|| Err(anyhow!("")), |s| Ok(s.to_string()))
|
||||
}
|
||||
|
||||
let mut term = env::var("TERM").unwrap_or_default();
|
||||
let mut program = env::var("TERM_PROGRAM").unwrap_or_default();
|
||||
|
||||
if *TMUX {
|
||||
term = tmux_env("TERM").unwrap_or(term);
|
||||
program = tmux_env("TERM_PROGRAM").unwrap_or(program);
|
||||
}
|
||||
|
||||
(term, program)
|
||||
}
|
||||
|
||||
pub fn via_csi() -> Result<Self> {
|
||||
enable_raw_mode()?;
|
||||
execute!(
|
||||
LineWriter::new(stderr()),
|
||||
SavePosition,
|
||||
Print("\x1b[>q\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\\x1b[c"),
|
||||
RestorePosition
|
||||
)?;
|
||||
|
||||
let resp = futures::executor::block_on(Term::read_until_da1())?;
|
||||
disable_raw_mode().ok();
|
||||
|
||||
let names = [
|
||||
("kitty", Self::Kitty),
|
||||
("Konsole", Self::Konsole),
|
||||
("iTerm2", Self::Iterm2),
|
||||
("WezTerm", Self::WezTerm),
|
||||
("foot", Self::Foot),
|
||||
("ghostty", Self::Ghostty),
|
||||
];
|
||||
|
||||
for (name, emulator) in names.iter() {
|
||||
if resp.contains(name) {
|
||||
return Ok(emulator.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let mut adapters = Vec::with_capacity(2);
|
||||
if resp.contains("\x1b_Gi=31;OK") {
|
||||
adapters.push(Adaptor::KittyOld);
|
||||
}
|
||||
if ["?4;", "?4c", ";4;", ";4c"].iter().any(|s| resp.contains(s)) {
|
||||
adapters.push(Adaptor::Sixel);
|
||||
}
|
||||
|
||||
Ok(Self::Unknown(adapters))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
use std::{io::Write, path::Path};
|
||||
|
||||
use anyhow::Result;
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use image::{codecs::jpeg::JpegEncoder, DynamicImage};
|
||||
use ratatui::layout::Rect;
|
||||
use yazi_shared::term::Term;
|
||||
|
||||
use super::image::Image;
|
||||
use crate::{adaptor::Adaptor, CLOSE, START};
|
||||
|
||||
pub(super) struct Iterm2;
|
||||
|
||||
impl Iterm2 {
|
||||
pub(super) async fn image_show(path: &Path, rect: Rect) -> Result<(u32, u32)> {
|
||||
let img = Image::downscale(path, rect).await?;
|
||||
let size = (img.width(), img.height());
|
||||
let b = Self::encode(img).await?;
|
||||
|
||||
Adaptor::Iterm2.image_hide()?;
|
||||
Adaptor::shown_store(rect, size);
|
||||
Term::move_lock((rect.x, rect.y), |stderr| {
|
||||
stderr.write_all(&b)?;
|
||||
Ok(size)
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn image_erase(rect: Rect) -> Result<()> {
|
||||
let s = " ".repeat(rect.width as usize);
|
||||
Term::move_lock((0, 0), |stderr| {
|
||||
for y in rect.top()..rect.bottom() {
|
||||
Term::move_to(stderr, rect.x, y)?;
|
||||
write!(stderr, "{s}")?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
async fn encode(img: DynamicImage) -> Result<Vec<u8>> {
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let size = (img.width(), img.height());
|
||||
|
||||
let mut jpg = vec![];
|
||||
JpegEncoder::new_with_quality(&mut jpg, 75).encode_image(&img)?;
|
||||
|
||||
let mut buf = vec![];
|
||||
write!(
|
||||
buf,
|
||||
"{}]1337;File=inline=1;size={};width={}px;height={}px;doNotMoveCursor=1:{}\x07{}",
|
||||
START,
|
||||
jpg.len(),
|
||||
size.0,
|
||||
size.1,
|
||||
general_purpose::STANDARD.encode(&jpg),
|
||||
CLOSE
|
||||
)?;
|
||||
Ok(buf)
|
||||
})
|
||||
.await?
|
||||
}
|
||||
}
|
||||
|
|
@ -9,17 +9,18 @@ homepage = "https://yazi-rs.github.io"
|
|||
repository = "https://github.com/sxyazi/yazi"
|
||||
|
||||
[dependencies]
|
||||
yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.5" }
|
||||
regex = "1.10.5"
|
||||
yazi-adapter = { path = "../yazi-adapter", version = "0.2.5" }
|
||||
yazi-config = { path = "../yazi-config", version = "0.2.5" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "0.2.5" }
|
||||
|
||||
# External dependencies
|
||||
clap = { version = "4.5.4", features = [ "derive" ] }
|
||||
serde = { version = "1.0.198", features = [ "derive" ] }
|
||||
clap = { version = "4.5.7", features = [ "derive" ] }
|
||||
serde = { version = "1.0.203", features = [ "derive" ] }
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "4.5.4", features = [ "derive" ] }
|
||||
clap_complete = "4.5.2"
|
||||
clap_complete_nushell = "4.5.1"
|
||||
clap_complete_fig = "4.5.0"
|
||||
clap = { version = "4.5.7", features = [ "derive" ] }
|
||||
clap_complete = "4.5.6"
|
||||
clap_complete_nushell = "4.5.2"
|
||||
clap_complete_fig = "4.5.1"
|
||||
vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::{collections::HashSet, env, ffi::OsString, fmt::Write, path::{Path, PathBuf}, process};
|
||||
use std::{collections::HashSet, env, ffi::{OsStr, OsString}, fmt::Write, path::{Path, PathBuf}, process};
|
||||
|
||||
use clap::Parser;
|
||||
use regex::Regex;
|
||||
use serde::Serialize;
|
||||
use yazi_config::PREVIEW;
|
||||
use yazi_shared::{fs::{current_cwd, expand_path}, Xdg};
|
||||
|
|
@ -37,6 +38,22 @@ impl Boot {
|
|||
(parent.unwrap().to_owned(), Some(entry.file_name().unwrap().to_owned()))
|
||||
}
|
||||
|
||||
fn process_output(name: impl AsRef<OsStr>, arg: impl AsRef<OsStr>) -> String {
|
||||
match std::process::Command::new(name.as_ref()).arg(arg).output() {
|
||||
Ok(out) if out.status.success() => {
|
||||
let line =
|
||||
String::from_utf8_lossy(&out.stdout).trim().lines().next().unwrap_or_default().to_owned();
|
||||
Regex::new(r"\d+\.\d+(\.\d+-\d+|\.\d+|\b)")
|
||||
.unwrap()
|
||||
.find(&line)
|
||||
.map(|m| m.as_str().to_owned())
|
||||
.unwrap_or(line)
|
||||
}
|
||||
Ok(out) => format!("{:?}, {:?}", out.status, String::from_utf8_lossy(&out.stderr)),
|
||||
Err(e) => format!("{e}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn action_version() -> String {
|
||||
format!(
|
||||
"{} ({} {})",
|
||||
|
|
@ -47,21 +64,24 @@ impl Boot {
|
|||
}
|
||||
|
||||
fn action_debug() -> Result<String, std::fmt::Error> {
|
||||
use std::{env::consts::{ARCH, FAMILY, OS}, process::Command};
|
||||
use std::env::consts::{ARCH, FAMILY, OS};
|
||||
let mut s = String::new();
|
||||
|
||||
writeln!(s, "\nYazi")?;
|
||||
writeln!(s, " Version: {}", Self::action_version())?;
|
||||
writeln!(s, " OS: {}-{} ({})", OS, ARCH, FAMILY)?;
|
||||
writeln!(s, " Debug : {}", cfg!(debug_assertions))?;
|
||||
writeln!(s, " OS : {}-{} ({})", OS, ARCH, FAMILY)?;
|
||||
|
||||
writeln!(s, "\nYa")?;
|
||||
writeln!(s, " Version: {}", Self::process_output("ya", "--version"))?;
|
||||
|
||||
writeln!(s, "\nEmulator")?;
|
||||
writeln!(s, " Emulator.via_env: {:?}", yazi_adaptor::Emulator::via_env())?;
|
||||
writeln!(s, " Emulator.via_csi: {:?}", yazi_adaptor::Emulator::via_csi())?;
|
||||
writeln!(s, " Emulator.detect: {:?}", yazi_adaptor::Emulator::detect())?;
|
||||
writeln!(s, " Emulator.via_env: {:?}", yazi_adapter::Emulator::via_env())?;
|
||||
writeln!(s, " Emulator.via_csi: {:?}", yazi_adapter::Emulator::via_csi())?;
|
||||
writeln!(s, " Emulator.detect : {:?}", yazi_adapter::Emulator::detect())?;
|
||||
|
||||
writeln!(s, "\nAdaptor")?;
|
||||
writeln!(s, " Adaptor.matches: {:?}", yazi_adaptor::Adaptor::matches())?;
|
||||
writeln!(s, "\nAdapter")?;
|
||||
writeln!(s, " Adapter.matches: {:?}", yazi_adapter::Adapter::matches())?;
|
||||
|
||||
writeln!(s, "\nDesktop")?;
|
||||
writeln!(s, " XDG_SESSION_TYPE: {:?}", env::var_os("XDG_SESSION_TYPE"))?;
|
||||
|
|
@ -85,13 +105,6 @@ impl Boot {
|
|||
writeln!(s, " YAZI_FILE_ONE : {:?}", env::var_os("YAZI_FILE_ONE"))?;
|
||||
writeln!(s, " YAZI_CONFIG_HOME : {:?}", env::var_os("YAZI_CONFIG_HOME"))?;
|
||||
|
||||
writeln!(s, "\nfile(1)")?;
|
||||
writeln!(
|
||||
s,
|
||||
" Version: {:?}",
|
||||
Command::new(env::var_os("YAZI_FILE_ONE").unwrap_or("file".into())).arg("--version").output()
|
||||
)?;
|
||||
|
||||
writeln!(s, "\nText Opener")?;
|
||||
writeln!(
|
||||
s,
|
||||
|
|
@ -101,10 +114,25 @@ impl Boot {
|
|||
writeln!(s, " block : {:?}", yazi_config::OPEN.block_opener("bulk.txt", "text/plain"))?;
|
||||
|
||||
writeln!(s, "\ntmux")?;
|
||||
writeln!(s, " TMUX: {:?}", *yazi_adaptor::TMUX)?;
|
||||
writeln!(s, " TMUX : {:?}", *yazi_adapter::TMUX)?;
|
||||
writeln!(s, " Version: {}", Self::process_output("tmux", "-V"))?;
|
||||
|
||||
writeln!(s, "\nUeberzug++")?;
|
||||
writeln!(s, " Version: {:?}", Command::new("ueberzugpp").arg("--version").output())?;
|
||||
writeln!(s, "\nDependencies")?;
|
||||
writeln!(
|
||||
s,
|
||||
" file : {}",
|
||||
Self::process_output(env::var_os("YAZI_FILE_ONE").unwrap_or("file".into()), "--version")
|
||||
)?;
|
||||
writeln!(s, " ueberzugpp : {}", Self::process_output("ueberzugpp", "--version"))?;
|
||||
writeln!(s, " ffmpegthumbnailer: {}", Self::process_output("ffmpegthumbnailer", "-v"))?;
|
||||
writeln!(s, " magick : {}", Self::process_output("magick", "--version"))?;
|
||||
writeln!(s, " fzf : {}", Self::process_output("fzf", "--version"))?;
|
||||
writeln!(s, " fd : {}", Self::process_output("fd", "--version"))?;
|
||||
writeln!(s, " rg : {}", Self::process_output("rg", "--version"))?;
|
||||
writeln!(s, " chafa : {}", Self::process_output("chafa", "--version"))?;
|
||||
writeln!(s, " zoxide : {}", Self::process_output("zoxide", "--version"))?;
|
||||
writeln!(s, " unar : {}", Self::process_output("unar", "--version"))?;
|
||||
writeln!(s, " jq : {}", Self::process_output("jq", "--version"))?;
|
||||
|
||||
writeln!(s, "\n\n--------------------------------------------------")?;
|
||||
writeln!(
|
||||
|
|
@ -145,7 +173,7 @@ impl Default for Boot {
|
|||
.map(|s| s.split(',').map(|s| s.to_owned()).collect())
|
||||
.unwrap_or_default();
|
||||
|
||||
let boot = Self {
|
||||
Self {
|
||||
cwd,
|
||||
file,
|
||||
|
||||
|
|
@ -156,11 +184,7 @@ impl Default for Boot {
|
|||
plugin_dir: config_dir.join("plugins"),
|
||||
config_dir,
|
||||
state_dir: Xdg::state_dir(),
|
||||
};
|
||||
|
||||
std::fs::create_dir_all(&boot.flavor_dir).ok();
|
||||
std::fs::create_dir_all(&boot.plugin_dir).ok();
|
||||
boot
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,21 +10,30 @@ repository = "https://github.com/sxyazi/yazi"
|
|||
|
||||
[dependencies]
|
||||
yazi-dds = { path = "../yazi-dds", version = "0.2.5" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "0.2.5" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = "1.0.82"
|
||||
clap = { version = "4.5.4", features = [ "derive" ] }
|
||||
serde_json = "1.0.116"
|
||||
tokio = { version = "1.37.0", features = [ "full" ] }
|
||||
anyhow = "1.0.86"
|
||||
clap = { version = "4.5.7", features = [ "derive" ] }
|
||||
crossterm = "0.27.0"
|
||||
md-5 = "0.10.6"
|
||||
serde_json = "1.0.117"
|
||||
tokio = { version = "1.38.0", features = [ "full" ] }
|
||||
toml_edit = "0.22.14"
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0.82"
|
||||
clap = { version = "4.5.4", features = [ "derive" ] }
|
||||
clap_complete = "4.5.2"
|
||||
clap_complete_fig = "4.5.0"
|
||||
clap_complete_nushell = "4.5.1"
|
||||
serde_json = "1.0.116"
|
||||
anyhow = "1.0.86"
|
||||
clap = { version = "4.5.7", features = [ "derive" ] }
|
||||
clap_complete = "4.5.6"
|
||||
clap_complete_fig = "4.5.1"
|
||||
clap_complete_nushell = "4.5.2"
|
||||
serde_json = "1.0.117"
|
||||
vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] }
|
||||
|
||||
[[bin]]
|
||||
name = "ya"
|
||||
path = "src/main.rs"
|
||||
|
||||
[package.metadata.binstall]
|
||||
pkg-url = "{ repo }/releases/download/v{ version }/yazi-{ target }{ archive-suffix }"
|
||||
bin-dir = "yazi-{ target }/{ bin }{ binary-ext }"
|
||||
|
|
|
|||
|
|
@ -5,8 +5,11 @@ use std::{env, error::Error};
|
|||
|
||||
use clap::CommandFactory;
|
||||
use clap_complete::{generate_to, Shell};
|
||||
use vergen::EmitBuilder;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
EmitBuilder::builder().build_date().git_sha(true).emit()?;
|
||||
|
||||
if env::var_os("YAZI_GEN_COMPLETIONS").is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,29 +4,34 @@ use anyhow::{bail, Result};
|
|||
use clap::{command, Parser, Subcommand};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "ya", version, about, long_about = None)]
|
||||
#[command(propagate_version = true)]
|
||||
#[command(name = "Ya", about, long_about = None)]
|
||||
pub(super) struct Args {
|
||||
#[command(subcommand)]
|
||||
pub(super) command: Command,
|
||||
|
||||
/// Print version
|
||||
#[arg(short = 'V', long)]
|
||||
pub(super) version: bool,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub(super) enum Command {
|
||||
/// Publish a message to remote instance(s).
|
||||
Pub(CommandPub),
|
||||
/// Publish a static message to all remote instances.
|
||||
PubStatic(CommandPubStatic),
|
||||
/// Manage packages.
|
||||
Pack(CommandPack),
|
||||
/// Subscribe to messages from all remote instances.
|
||||
Sub(CommandSub),
|
||||
}
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub(super) struct CommandPub {
|
||||
/// The receiver ID.
|
||||
#[arg(index = 1)]
|
||||
pub(super) receiver: u64,
|
||||
/// The kind of message.
|
||||
#[arg(index = 2)]
|
||||
#[arg(index = 1)]
|
||||
pub(super) kind: String,
|
||||
/// The receiver ID.
|
||||
#[arg(index = 2)]
|
||||
pub(super) receiver: Option<u64>,
|
||||
/// Send the message with a string body.
|
||||
#[arg(long)]
|
||||
pub(super) str: Option<String>,
|
||||
|
|
@ -37,34 +42,16 @@ pub(super) struct CommandPub {
|
|||
|
||||
impl CommandPub {
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn body(&self) -> Result<Cow<str>> {
|
||||
if let Some(json) = &self.json {
|
||||
Ok(json.into())
|
||||
} else if let Some(str) = &self.str {
|
||||
Ok(serde_json::to_string(str)?.into())
|
||||
pub(super) fn receiver(&self) -> Result<u64> {
|
||||
if let Some(receiver) = self.receiver {
|
||||
Ok(receiver)
|
||||
} else if let Some(s) = std::env::var("YAZI_PID").ok().filter(|s| !s.is_empty()) {
|
||||
Ok(s.parse()?)
|
||||
} else {
|
||||
bail!("No body provided");
|
||||
}
|
||||
bail!("No receiver ID provided, also no YAZI_ID environment variable found.")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub(super) struct CommandPubStatic {
|
||||
/// The severity of the message.
|
||||
#[arg(index = 1)]
|
||||
pub(super) severity: u16,
|
||||
/// The kind of message.
|
||||
#[arg(index = 2)]
|
||||
pub(super) kind: String,
|
||||
/// Send the message with a string body.
|
||||
#[arg(long)]
|
||||
pub(super) str: Option<String>,
|
||||
/// Send the message with a JSON body.
|
||||
#[arg(long)]
|
||||
pub(super) json: Option<String>,
|
||||
}
|
||||
|
||||
impl CommandPubStatic {
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn body(&self) -> Result<Cow<str>> {
|
||||
if let Some(json) = &self.json {
|
||||
|
|
@ -72,7 +59,31 @@ impl CommandPubStatic {
|
|||
} else if let Some(str) = &self.str {
|
||||
Ok(serde_json::to_string(str)?.into())
|
||||
} else {
|
||||
bail!("No body provided");
|
||||
Ok("".into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(clap::Args)]
|
||||
#[command(arg_required_else_help = true)]
|
||||
pub(super) struct CommandPack {
|
||||
/// Add a package.
|
||||
#[arg(short = 'a', long)]
|
||||
pub(super) add: Option<String>,
|
||||
/// Install all packages.
|
||||
#[arg(short = 'i', long)]
|
||||
pub(super) install: bool,
|
||||
/// List all packages.
|
||||
#[arg(short = 'l', long)]
|
||||
pub(super) list: bool,
|
||||
/// Upgrade all packages.
|
||||
#[arg(short = 'u', long)]
|
||||
pub(super) upgrade: bool,
|
||||
}
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub(super) struct CommandSub {
|
||||
/// The kind of messages to subscribe to, separated by commas if multiple.
|
||||
#[arg(index = 1)]
|
||||
pub(super) kinds: String,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,50 @@
|
|||
mod args;
|
||||
mod package;
|
||||
|
||||
use args::*;
|
||||
use clap::Parser;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let args = Args::parse();
|
||||
if std::env::args_os().nth(1).is_some_and(|s| s == "-V" || s == "--version") {
|
||||
println!(
|
||||
"Ya {} ({} {})",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
env!("VERGEN_GIT_SHA"),
|
||||
env!("VERGEN_BUILD_DATE")
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match &args.command {
|
||||
match Args::parse().command {
|
||||
Command::Pub(cmd) => {
|
||||
yazi_dds::init();
|
||||
if let Err(e) = yazi_dds::Client::shot(&cmd.kind, cmd.receiver, None, &cmd.body()?).await {
|
||||
if let Err(e) = yazi_dds::Client::shot(&cmd.kind, cmd.receiver()?, &cmd.body()?).await {
|
||||
eprintln!("Cannot send message: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
Command::PubStatic(cmd) => {
|
||||
Command::Pack(cmd) => {
|
||||
package::init();
|
||||
if cmd.install {
|
||||
package::Package::install_from_config("plugin", false).await?;
|
||||
package::Package::install_from_config("flavor", false).await?;
|
||||
} else if cmd.list {
|
||||
package::Package::list_from_config("plugin").await?;
|
||||
package::Package::list_from_config("flavor").await?;
|
||||
} else if cmd.upgrade {
|
||||
package::Package::install_from_config("plugin", true).await?;
|
||||
package::Package::install_from_config("flavor", true).await?;
|
||||
} else if let Some(repo) = &cmd.add {
|
||||
package::Package::add_to_config(repo).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Command::Sub(cmd) => {
|
||||
yazi_dds::init();
|
||||
if let Err(e) = yazi_dds::Client::shot(&cmd.kind, 0, Some(cmd.severity), &cmd.body()?).await {
|
||||
eprintln!("Cannot send message: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
yazi_dds::Client::draw(cmd.kinds.split(',').collect()).await?;
|
||||
|
||||
tokio::signal::ctrl_c().await?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
20
yazi-cli/src/package/add.rs
Normal file
20
yazi-cli/src/package/add.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
use anyhow::Result;
|
||||
use yazi_shared::fs::must_exists;
|
||||
|
||||
use super::{Git, Package};
|
||||
|
||||
impl Package {
|
||||
pub(super) async fn add(&mut self) -> Result<()> {
|
||||
self.output("Upgrading package `{name}`")?;
|
||||
|
||||
let path = self.local();
|
||||
if !must_exists(&path).await {
|
||||
Git::clone(&self.remote(), &path).await?;
|
||||
} else {
|
||||
Git::pull(&path).await?;
|
||||
};
|
||||
|
||||
self.commit = Git::hash(&path).await?;
|
||||
self.deploy().await
|
||||
}
|
||||
}
|
||||
50
yazi-cli/src/package/deploy.rs
Normal file
50
yazi-cli/src/package/deploy.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
use anyhow::{bail, Context, Result};
|
||||
use tokio::fs;
|
||||
use yazi_shared::{fs::{maybe_exists, must_exists}, Xdg};
|
||||
|
||||
use super::Package;
|
||||
|
||||
const TRACKER: &str = "DO_NOT_MODIFY_ANYTHING_IN_THIS_DIRECTORY";
|
||||
|
||||
impl Package {
|
||||
pub(super) async fn deploy(&mut self) -> Result<()> {
|
||||
let Some(name) = self.name().map(ToOwned::to_owned) else { bail!("Invalid package url") };
|
||||
let from = self.local().join(&self.child);
|
||||
|
||||
self.output("Deploying package `{name}`")?;
|
||||
self.is_flavor = maybe_exists(&from.join("flavor.toml")).await;
|
||||
let to = if self.is_flavor {
|
||||
Xdg::config_dir().join(format!("flavors/{name}"))
|
||||
} else {
|
||||
Xdg::config_dir().join(format!("plugins/{name}"))
|
||||
};
|
||||
|
||||
let tracker = to.join(TRACKER);
|
||||
if maybe_exists(&to).await && !must_exists(&tracker).await {
|
||||
bail!(
|
||||
"A user package with the same name `{name}` already exists.
|
||||
For safety, please manually delete it from your plugin/flavor directory and re-run the command."
|
||||
);
|
||||
}
|
||||
|
||||
fs::create_dir_all(&to).await?;
|
||||
fs::write(tracker, []).await?;
|
||||
|
||||
let files = if self.is_flavor {
|
||||
&["flavor.toml", "tmtheme.xml", "README.md", "preview.png", "LICENSE", "LICENSE-tmtheme"][..]
|
||||
} else {
|
||||
&["init.lua", "README.md", "LICENSE"][..]
|
||||
};
|
||||
|
||||
for file in files {
|
||||
let (from, to) = (from.join(file), to.join(file));
|
||||
|
||||
fs::copy(&from, &to)
|
||||
.await
|
||||
.with_context(|| format!("Failed to copy `{}` to `{}`", from.display(), to.display()))?;
|
||||
}
|
||||
|
||||
println!("Done!");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
55
yazi-cli/src/package/git.rs
Normal file
55
yazi-cli/src/package/git.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
use std::path::Path;
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use tokio::process::Command;
|
||||
use yazi_shared::strip_trailing_newline;
|
||||
|
||||
pub(super) struct Git;
|
||||
|
||||
impl Git {
|
||||
pub(super) async fn clone(url: &str, path: &Path) -> Result<()> {
|
||||
Self::exec(|c| c.args(["clone", url]).arg(path)).await
|
||||
}
|
||||
|
||||
pub(super) async fn fetch(path: &Path) -> Result<()> {
|
||||
Self::exec(|c| c.current_dir(path).arg("fetch")).await
|
||||
}
|
||||
|
||||
pub(super) async fn checkout(path: &Path, commit: &str) -> Result<()> {
|
||||
Self::exec(|c| c.current_dir(path).args(["checkout", commit])).await
|
||||
}
|
||||
|
||||
pub(super) async fn pull(path: &Path) -> Result<()> {
|
||||
Self::fetch(path).await?;
|
||||
Self::checkout(path, "origin/HEAD").await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) async fn hash(path: &Path) -> Result<String> {
|
||||
let output = Command::new("git")
|
||||
.current_dir(path)
|
||||
.args(["rev-parse", "--short", "HEAD"])
|
||||
.output()
|
||||
.await
|
||||
.context("Failed to get current commit hash")?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!("Getting commit hash failed: {}", output.status);
|
||||
}
|
||||
|
||||
Ok(strip_trailing_newline(
|
||||
String::from_utf8(output.stdout).context("Failed to parse commit hash")?,
|
||||
))
|
||||
}
|
||||
|
||||
async fn exec(f: impl FnOnce(&mut Command) -> &mut Command) -> Result<()> {
|
||||
let status =
|
||||
f(&mut Command::new("git")).status().await.context("Failed to execute `git` command")?;
|
||||
|
||||
if !status.success() {
|
||||
bail!("`git` command failed: {status}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
25
yazi-cli/src/package/install.rs
Normal file
25
yazi-cli/src/package/install.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
use anyhow::Result;
|
||||
use yazi_shared::fs::must_exists;
|
||||
|
||||
use super::{Git, Package};
|
||||
|
||||
impl Package {
|
||||
pub(super) async fn install(&mut self) -> Result<()> {
|
||||
self.output("Installing package `{name}`")?;
|
||||
|
||||
let path = self.local();
|
||||
if !must_exists(&path).await {
|
||||
Git::clone(&self.remote(), &path).await?;
|
||||
} else {
|
||||
Git::fetch(&path).await?;
|
||||
};
|
||||
|
||||
if self.commit.is_empty() {
|
||||
self.commit = Git::hash(&path).await?;
|
||||
} else {
|
||||
Git::checkout(&path, self.commit.trim_start_matches('=')).await?;
|
||||
}
|
||||
|
||||
self.deploy().await
|
||||
}
|
||||
}
|
||||
17
yazi-cli/src/package/mod.rs
Normal file
17
yazi-cli/src/package/mod.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#![allow(clippy::module_inception)]
|
||||
|
||||
mod add;
|
||||
mod deploy;
|
||||
mod git;
|
||||
mod install;
|
||||
mod package;
|
||||
mod parser;
|
||||
mod upgrade;
|
||||
|
||||
use git::*;
|
||||
pub(super) use package::*;
|
||||
|
||||
pub(super) fn init() {
|
||||
let root = yazi_shared::Xdg::state_dir().join("packages");
|
||||
std::fs::create_dir_all(root).expect("Failed to create packages directory");
|
||||
}
|
||||
79
yazi-cli/src/package/package.rs
Normal file
79
yazi-cli/src/package/package.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
use std::{borrow::Cow, io::BufWriter, path::PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use md5::{Digest, Md5};
|
||||
use yazi_shared::Xdg;
|
||||
|
||||
pub(crate) struct Package {
|
||||
pub(crate) repo: String,
|
||||
pub(crate) child: String,
|
||||
pub(crate) commit: String,
|
||||
pub(super) is_flavor: bool,
|
||||
}
|
||||
|
||||
impl Package {
|
||||
pub(super) fn new(url: &str, commit: Option<&str>) -> Self {
|
||||
let mut parts = url.splitn(2, '#');
|
||||
|
||||
let mut repo = parts.next().unwrap_or_default().to_owned();
|
||||
let child = if let Some(s) = parts.next() {
|
||||
format!("{s}.yazi")
|
||||
} else {
|
||||
repo.push_str(".yazi");
|
||||
String::new()
|
||||
};
|
||||
|
||||
Self { repo, child, commit: commit.unwrap_or_default().to_owned(), is_flavor: false }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn use_(&self) -> Cow<str> {
|
||||
if self.child.is_empty() {
|
||||
self.repo.trim_end_matches(".yazi").into()
|
||||
} else {
|
||||
format!("{}#{}", self.repo, self.child.trim_end_matches(".yazi")).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn name(&self) -> Option<&str> {
|
||||
let s = if self.child.is_empty() {
|
||||
self.repo.split('/').last().filter(|s| !s.is_empty())
|
||||
} else {
|
||||
Some(self.child.as_str())
|
||||
};
|
||||
|
||||
s.filter(|s| s.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'z' | b'-' | b'.')))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn local(&self) -> PathBuf {
|
||||
Xdg::state_dir()
|
||||
.join("packages")
|
||||
.join(format!("{:x}", Md5::new_with_prefix(self.remote()).finalize()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn remote(&self) -> String {
|
||||
// Support more Git hosting services in the future
|
||||
format!("https://github.com/{}.git", self.repo)
|
||||
}
|
||||
|
||||
pub(super) fn output(&self, s: &str) -> Result<()> {
|
||||
use crossterm::style::{Attribute, Print, SetAttributes};
|
||||
|
||||
crossterm::execute!(
|
||||
BufWriter::new(std::io::stdout()),
|
||||
Print("\n"),
|
||||
SetAttributes(Attribute::Reverse.into()),
|
||||
SetAttributes(Attribute::Bold.into()),
|
||||
Print(" "),
|
||||
Print(s.replacen("{name}", self.name().unwrap_or_default(), 1)),
|
||||
Print(" "),
|
||||
SetAttributes(Attribute::NoBold.into()),
|
||||
SetAttributes(Attribute::NoReverse.into()),
|
||||
Print("\n\n"),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
135
yazi-cli/src/package/parser.rs
Normal file
135
yazi-cli/src/package/parser.rs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
use anyhow::{bail, Context, Result};
|
||||
use tokio::fs;
|
||||
use toml_edit::{Array, DocumentMut, InlineTable, Item, Value};
|
||||
use yazi_shared::Xdg;
|
||||
|
||||
use super::Package;
|
||||
|
||||
impl Package {
|
||||
pub(crate) async fn add_to_config(use_: &str) -> Result<()> {
|
||||
let mut package = Self::new(use_, None);
|
||||
let Some(name) = package.name() else { bail!("Invalid package `use`") };
|
||||
|
||||
let path = Xdg::config_dir().join("package.toml");
|
||||
let mut doc = Self::ensure_config(&fs::read_to_string(&path).await.unwrap_or_default())?;
|
||||
|
||||
Self::ensure_unique(&doc, name)?;
|
||||
package.add().await?;
|
||||
|
||||
let mut table = InlineTable::new();
|
||||
table.insert("use", package.use_().as_ref().into());
|
||||
if !package.commit.is_empty() {
|
||||
table.insert("commit", package.commit.into());
|
||||
}
|
||||
|
||||
if package.is_flavor {
|
||||
doc["flavor"]["deps"].as_array_mut().unwrap().push(table);
|
||||
} else {
|
||||
doc["plugin"]["deps"].as_array_mut().unwrap().push(table);
|
||||
}
|
||||
|
||||
fs::write(path, doc.to_string()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn install_from_config(section: &str, upgrade: bool) -> Result<()> {
|
||||
let path = Xdg::config_dir().join("package.toml");
|
||||
let Ok(s) = fs::read_to_string(&path).await else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut doc = s.parse::<DocumentMut>().context("Failed to parse package.toml")?;
|
||||
let Some(deps) = doc.get_mut(section).and_then(|d| d.get_mut("deps")) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let deps = deps.as_array_mut().context("`deps` must be an array")?;
|
||||
for dep in deps.iter_mut() {
|
||||
let dep = dep.as_inline_table_mut().context("Dependency must be an inline table")?;
|
||||
let use_ = dep.get("use").and_then(|d| d.as_str()).context("Missing `use` field")?;
|
||||
let commit = dep.get("commit").and_then(|d| d.as_str());
|
||||
|
||||
let mut package = Package::new(use_, commit);
|
||||
if upgrade {
|
||||
package.upgrade().await?;
|
||||
} else {
|
||||
package.install().await?;
|
||||
}
|
||||
|
||||
if package.commit.is_empty() {
|
||||
dep.remove("commit");
|
||||
} else {
|
||||
dep.insert("commit", package.commit.into());
|
||||
}
|
||||
}
|
||||
|
||||
fs::write(path, doc.to_string()).await.context("Failed to write package.toml")
|
||||
}
|
||||
|
||||
pub(crate) async fn list_from_config(section: &str) -> Result<()> {
|
||||
let path = Xdg::config_dir().join("package.toml");
|
||||
let Ok(s) = fs::read_to_string(&path).await else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let doc = s.parse::<DocumentMut>().context("Failed to parse package.toml")?;
|
||||
let Some(deps) = doc.get(section).and_then(|d| d.get("deps")) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let deps = deps.as_array().context("`deps` must be an array")?;
|
||||
println!("{section}s:");
|
||||
|
||||
for dep in deps {
|
||||
if let Some(Value::String(use_)) = dep.as_inline_table().and_then(|t| t.get("use")) {
|
||||
println!("\t{}", use_.value());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_config(s: &str) -> Result<DocumentMut> {
|
||||
let mut doc = s.parse::<DocumentMut>().context("Failed to parse package.toml")?;
|
||||
|
||||
doc
|
||||
.entry("plugin")
|
||||
.or_insert(toml_edit::table())
|
||||
.as_table_mut()
|
||||
.context("Failed to get `plugin` table")?
|
||||
.entry("deps")
|
||||
.or_insert(Item::Value(Array::new().into()))
|
||||
.as_array()
|
||||
.context("Failed to get `deps` array")?;
|
||||
|
||||
doc
|
||||
.entry("flavor")
|
||||
.or_insert(toml_edit::table())
|
||||
.as_table_mut()
|
||||
.context("Failed to get `flavor` table")?
|
||||
.entry("deps")
|
||||
.or_insert(Item::Value(Array::new().into()))
|
||||
.as_array()
|
||||
.context("Failed to get `deps` array")?;
|
||||
|
||||
Ok(doc)
|
||||
}
|
||||
|
||||
fn ensure_unique(doc: &DocumentMut, name: &str) -> Result<()> {
|
||||
#[inline]
|
||||
fn same(v: &Value, name: &str) -> bool {
|
||||
v.as_inline_table()
|
||||
.and_then(|t| t.get("use"))
|
||||
.and_then(|v| v.as_str())
|
||||
.is_some_and(|s| Package::new(s, None).name() == Some(name))
|
||||
}
|
||||
|
||||
if doc["plugin"]["deps"].as_array().unwrap().into_iter().any(|v| same(v, name)) {
|
||||
bail!("Plugin `{name}` already exists in package.toml");
|
||||
}
|
||||
if doc["flavor"]["deps"].as_array().unwrap().into_iter().any(|v| same(v, name)) {
|
||||
bail!("Flavor `{name}` already exists in package.toml");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
9
yazi-cli/src/package/upgrade.rs
Normal file
9
yazi-cli/src/package/upgrade.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use anyhow::Result;
|
||||
|
||||
use super::Package;
|
||||
|
||||
impl Package {
|
||||
pub(super) async fn upgrade(&mut self) -> Result<()> {
|
||||
if self.commit.starts_with('=') { Ok(()) } else { self.add().await }
|
||||
}
|
||||
}
|
||||
|
|
@ -12,13 +12,13 @@ repository = "https://github.com/sxyazi/yazi"
|
|||
yazi-shared = { path = "../yazi-shared", version = "0.2.5" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = "1.0.82"
|
||||
anyhow = "1.0.86"
|
||||
arc-swap = "1.7.1"
|
||||
bitflags = "2.5.0"
|
||||
crossterm = "0.27.0"
|
||||
globset = "0.4.14"
|
||||
indexmap = "2.2.6"
|
||||
ratatui = "=0.26.1"
|
||||
serde = { version = "1.0.198", features = [ "derive" ] }
|
||||
shell-words = "1.1.0"
|
||||
toml = { version = "0.8.12", features = [ "preserve_order" ] }
|
||||
ratatui = "0.27.0"
|
||||
serde = { version = "1.0.203", features = [ "derive" ] }
|
||||
toml = { version = "0.8.14", features = [ "preserve_order" ] }
|
||||
validator = { version = "0.18.1", features = [ "derive" ] }
|
||||
|
|
|
|||
|
|
@ -5,305 +5,305 @@
|
|||
[manager]
|
||||
|
||||
keymap = [
|
||||
{ on = [ "<Esc>" ], run = "escape", desc = "Exit visual mode, clear selected, or cancel search" },
|
||||
{ on = [ "<C-[>" ], run = "escape", desc = "Exit visual mode, clear selected, or cancel search" },
|
||||
{ on = [ "q" ], run = "quit", desc = "Exit the process" },
|
||||
{ on = [ "Q" ], run = "quit --no-cwd-file", desc = "Exit the process without writing cwd-file" },
|
||||
{ on = [ "<C-q>" ], run = "close", desc = "Close the current tab, or quit if it is last tab" },
|
||||
{ on = [ "<C-z>" ], run = "suspend", desc = "Suspend the process" },
|
||||
{ on = "<Esc>", run = "escape", desc = "Exit visual mode, clear selected, or cancel search" },
|
||||
{ on = "<C-[>", run = "escape", desc = "Exit visual mode, clear selected, or cancel search" },
|
||||
{ on = "q", run = "quit", desc = "Exit the process" },
|
||||
{ on = "Q", run = "quit --no-cwd-file", desc = "Exit the process without writing cwd-file" },
|
||||
{ on = "<C-c>", run = "close", desc = "Close the current tab, or quit if it is last tab" },
|
||||
{ on = "<C-z>", run = "suspend", desc = "Suspend the process" },
|
||||
|
||||
# Navigation
|
||||
{ on = [ "k" ], run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = [ "j" ], run = "arrow 1", desc = "Move cursor down" },
|
||||
{ on = "k", run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = "j", run = "arrow 1", desc = "Move cursor down" },
|
||||
|
||||
{ on = [ "K" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
|
||||
{ on = [ "J" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
|
||||
{ on = "K", run = "arrow -5", desc = "Move cursor up 5 lines" },
|
||||
{ on = "J", run = "arrow 5", desc = "Move cursor down 5 lines" },
|
||||
|
||||
{ on = [ "<S-Up>" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
|
||||
{ on = [ "<S-Down>" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
|
||||
{ on = "<S-Up>", run = "arrow -5", desc = "Move cursor up 5 lines" },
|
||||
{ on = "<S-Down>", run = "arrow 5", desc = "Move cursor down 5 lines" },
|
||||
|
||||
{ on = [ "<C-u>" ], run = "arrow -50%", desc = "Move cursor up half page" },
|
||||
{ on = [ "<C-d>" ], run = "arrow 50%", desc = "Move cursor down half page" },
|
||||
{ on = [ "<C-b>" ], run = "arrow -100%", desc = "Move cursor up one page" },
|
||||
{ on = [ "<C-f>" ], run = "arrow 100%", desc = "Move cursor down one page" },
|
||||
{ on = "<C-u>", run = "arrow -50%", desc = "Move cursor up half page" },
|
||||
{ on = "<C-d>", run = "arrow 50%", desc = "Move cursor down half page" },
|
||||
{ on = "<C-b>", run = "arrow -100%", desc = "Move cursor up one page" },
|
||||
{ on = "<C-f>", run = "arrow 100%", desc = "Move cursor down one page" },
|
||||
|
||||
{ on = [ "<C-PageUp>" ], run = "arrow -50%", desc = "Move cursor up half page" },
|
||||
{ on = [ "<C-PageDown>" ], run = "arrow 50%", desc = "Move cursor down half page" },
|
||||
{ on = [ "<PageUp>" ], run = "arrow -100%", desc = "Move cursor up one page" },
|
||||
{ on = [ "<PageDown>" ], run = "arrow 100%", desc = "Move cursor down one page" },
|
||||
{ on = "<C-PageUp>", run = "arrow -50%", desc = "Move cursor up half page" },
|
||||
{ on = "<C-PageDown>", run = "arrow 50%", desc = "Move cursor down half page" },
|
||||
{ on = "<PageUp>", run = "arrow -100%", desc = "Move cursor up one page" },
|
||||
{ on = "<PageDown>", run = "arrow 100%", desc = "Move cursor down one page" },
|
||||
|
||||
{ on = [ "h" ], run = "leave", desc = "Go back to the parent directory" },
|
||||
{ on = [ "l" ], run = "enter", desc = "Enter the child directory" },
|
||||
{ on = "h", run = "leave", desc = "Go back to the parent directory" },
|
||||
{ on = "l", run = "enter", desc = "Enter the child directory" },
|
||||
|
||||
{ on = [ "H" ], run = "back", desc = "Go back to the previous directory" },
|
||||
{ on = [ "L" ], run = "forward", desc = "Go forward to the next directory" },
|
||||
{ on = "H", run = "back", desc = "Go back to the previous directory" },
|
||||
{ on = "L", run = "forward", desc = "Go forward to the next directory" },
|
||||
|
||||
{ on = [ "<A-k>" ], run = "seek -5", desc = "Seek up 5 units in the preview" },
|
||||
{ on = [ "<A-j>" ], run = "seek 5", desc = "Seek down 5 units in the preview" },
|
||||
{ on = [ "<A-PageUp>" ], run = "seek -5", desc = "Seek up 5 units in the preview" },
|
||||
{ on = [ "<A-PageDown>" ], run = "seek 5", desc = "Seek down 5 units in the preview" },
|
||||
{ on = "<A-k>", run = "seek -5", desc = "Seek up 5 units in the preview" },
|
||||
{ on = "<A-j>", run = "seek 5", desc = "Seek down 5 units in the preview" },
|
||||
{ on = "<A-PageUp>", run = "seek -5", desc = "Seek up 5 units in the preview" },
|
||||
{ on = "<A-PageDown>", run = "seek 5", desc = "Seek down 5 units in the preview" },
|
||||
|
||||
{ on = [ "<Up>" ], run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = [ "<Down>" ], run = "arrow 1", desc = "Move cursor down" },
|
||||
{ on = [ "<Left>" ], run = "leave", desc = "Go back to the parent directory" },
|
||||
{ on = [ "<Right>" ], run = "enter", desc = "Enter the child directory" },
|
||||
{ on = "<Up>", run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = "<Down>", run = "arrow 1", desc = "Move cursor down" },
|
||||
{ on = "<Left>", run = "leave", desc = "Go back to the parent directory" },
|
||||
{ on = "<Right>", run = "enter", desc = "Enter the child directory" },
|
||||
|
||||
{ on = [ "g", "g" ], run = "arrow -99999999", desc = "Move cursor to the top" },
|
||||
{ on = [ "G" ], run = "arrow 99999999", desc = "Move cursor to the bottom" },
|
||||
{ on = "G", run = "arrow 99999999", desc = "Move cursor to the bottom" },
|
||||
|
||||
# Selection
|
||||
{ on = [ "<Space>" ], run = [ "select --state=none", "arrow 1" ], desc = "Toggle the current selection state" },
|
||||
{ on = [ "v" ], run = "visual_mode", desc = "Enter visual mode (selection mode)" },
|
||||
{ on = [ "V" ], run = "visual_mode --unset", desc = "Enter visual mode (unset mode)" },
|
||||
{ on = [ "<C-a>" ], run = "select_all --state=true", desc = "Select all files" },
|
||||
{ on = [ "<C-r>" ], run = "select_all --state=none", desc = "Inverse selection of all files" },
|
||||
{ on = "<Space>", run = [ "select --state=none", "arrow 1" ], desc = "Toggle the current selection state" },
|
||||
{ on = "v", run = "visual_mode", desc = "Enter visual mode (selection mode)" },
|
||||
{ on = "V", run = "visual_mode --unset", desc = "Enter visual mode (unset mode)" },
|
||||
{ on = "<C-a>", run = "select_all --state=true", desc = "Select all files" },
|
||||
{ on = "<C-r>", run = "select_all --state=none", desc = "Inverse selection of all files" },
|
||||
|
||||
# Operation
|
||||
{ on = [ "o" ], run = "open", desc = "Open the selected files" },
|
||||
{ on = [ "O" ], run = "open --interactive", desc = "Open the selected files interactively" },
|
||||
{ on = [ "<Enter>" ], run = "open", desc = "Open the selected files" },
|
||||
{ on = [ "<C-Enter>" ], run = "open --interactive", desc = "Open the selected files interactively" },
|
||||
{ on = [ "y" ], run = "yank", desc = "Copy the selected files" },
|
||||
{ on = [ "Y" ], run = "unyank", desc = "Cancel the yank status of files" },
|
||||
{ on = [ "x" ], run = "yank --cut", desc = "Cut the selected files" },
|
||||
{ on = [ "X" ], run = "unyank", desc = "Cancel the yank status of files" },
|
||||
{ on = [ "p" ], run = "paste", desc = "Paste the files" },
|
||||
{ on = [ "P" ], run = "paste --force", desc = "Paste the files (overwrite if the destination exists)" },
|
||||
{ on = [ "-" ], run = "link", desc = "Symlink the absolute path of files" },
|
||||
{ on = [ "_" ], run = "link --relative", desc = "Symlink the relative path of files" },
|
||||
{ on = [ "d" ], run = "remove", desc = "Move the files to the trash" },
|
||||
{ on = [ "D" ], run = "remove --permanently", desc = "Permanently delete the files" },
|
||||
{ on = [ "a" ], run = "create", desc = "Create a file or directory (ends with / for directories)" },
|
||||
{ on = [ "r" ], run = "rename --cursor=before_ext", desc = "Rename a file or directory" },
|
||||
{ on = [ ";" ], run = "shell", desc = "Run a shell command" },
|
||||
{ on = [ ":" ], run = "shell --block", desc = "Run a shell command (block the UI until the command finishes)" },
|
||||
{ on = [ "." ], run = "hidden toggle", desc = "Toggle the visibility of hidden files" },
|
||||
{ on = [ "s" ], run = "search fd", desc = "Search files by name using fd" },
|
||||
{ on = [ "S" ], run = "search rg", desc = "Search files by content using ripgrep" },
|
||||
{ on = [ "<C-s>" ], run = "search none", desc = "Cancel the ongoing search" },
|
||||
{ on = [ "z" ], run = "plugin zoxide", desc = "Jump to a directory using zoxide" },
|
||||
{ on = [ "Z" ], run = "plugin fzf", desc = "Jump to a directory, or reveal a file using fzf" },
|
||||
{ on = "o", run = "open", desc = "Open selected files" },
|
||||
{ on = "O", run = "open --interactive", desc = "Open selected files interactively" },
|
||||
{ on = "<Enter>", run = "open", desc = "Open selected files" },
|
||||
{ on = "<S-Enter>", run = "open --interactive", desc = "Open selected files interactively" },
|
||||
{ on = "y", run = "yank", desc = "Copy selected files" },
|
||||
{ on = "x", run = "yank --cut", desc = "Cut the selected files" },
|
||||
{ on = "Y", run = "unyank", desc = "Cancel the yank status" },
|
||||
{ on = "X", run = "unyank", desc = "Cancel the yank status" },
|
||||
{ on = "p", run = "paste", desc = "Paste yanked files" },
|
||||
{ on = "P", run = "paste --force", desc = "Paste yanked files (overwrite if the destination exists)" },
|
||||
{ on = "-", run = "link", desc = "Symlink the absolute path of yanked files" },
|
||||
{ on = "_", run = "link --relative", desc = "Symlink the relative path of yanked files" },
|
||||
{ on = "d", run = "remove", desc = "Trash selected files" },
|
||||
{ on = "D", run = "remove --permanently", desc = "Permanently delete selected files" },
|
||||
{ on = "a", run = "create", desc = "Create a file (ends with / for directories)" },
|
||||
{ on = "r", run = "rename --cursor=before_ext", desc = "Rename selected file(s)" },
|
||||
{ on = ";", run = "shell --interactive", desc = "Run a shell command" },
|
||||
{ on = ":", run = "shell --block --interactive", desc = "Run a shell command (block until finishes)" },
|
||||
{ on = ".", run = "hidden toggle", desc = "Toggle the visibility of hidden files" },
|
||||
{ on = "s", run = "search fd", desc = "Search files by name using fd" },
|
||||
{ on = "S", run = "search rg", desc = "Search files by content using ripgrep" },
|
||||
{ on = "<C-s>", run = "search none", desc = "Cancel the ongoing search" },
|
||||
{ on = "z", run = "plugin zoxide", desc = "Jump to a directory using zoxide" },
|
||||
{ on = "Z", run = "plugin fzf", desc = "Jump to a directory or reveal a file using fzf" },
|
||||
|
||||
# Linemode
|
||||
{ on = [ "m", "s" ], run = "linemode size", desc = "Set linemode to size" },
|
||||
{ on = [ "m", "p" ], run = "linemode permissions", desc = "Set linemode to permissions" },
|
||||
{ on = [ "m", "m" ], run = "linemode mtime", desc = "Set linemode to mtime" },
|
||||
{ on = [ "m", "o" ], run = "linemode owner", desc = "Set linemode to owner" },
|
||||
{ on = [ "m", "n" ], run = "linemode none", desc = "Set linemode to none" },
|
||||
|
||||
# Copy
|
||||
{ on = [ "c", "c" ], run = "copy path", desc = "Copy the absolute path" },
|
||||
{ on = [ "c", "d" ], run = "copy dirname", desc = "Copy the path of the parent directory" },
|
||||
{ on = [ "c", "f" ], run = "copy filename", desc = "Copy the name of the file" },
|
||||
{ on = [ "c", "n" ], run = "copy name_without_ext", desc = "Copy the name of the file without the extension" },
|
||||
{ on = [ "c", "c" ], run = "copy path", desc = "Copy the file path" },
|
||||
{ on = [ "c", "d" ], run = "copy dirname", desc = "Copy the directory path" },
|
||||
{ on = [ "c", "f" ], run = "copy filename", desc = "Copy the filename" },
|
||||
{ on = [ "c", "n" ], run = "copy name_without_ext", desc = "Copy the filename without extension" },
|
||||
|
||||
# Filter
|
||||
{ on = [ "f" ], run = "filter --smart", desc = "Filter the files" },
|
||||
{ on = "f", run = "filter --smart", desc = "Filter files" },
|
||||
|
||||
# Find
|
||||
{ on = [ "/" ], run = "find --smart", desc = "Find next file" },
|
||||
{ on = [ "?" ], run = "find --previous --smart", desc = "Find previous file" },
|
||||
{ on = [ "n" ], run = "find_arrow", desc = "Go to next found file" },
|
||||
{ on = [ "N" ], run = "find_arrow --previous", desc = "Go to previous found file" },
|
||||
{ on = "/", run = "find --smart", desc = "Find next file" },
|
||||
{ on = "?", run = "find --previous --smart", desc = "Find previous file" },
|
||||
{ on = "n", run = "find_arrow", desc = "Go to the next found" },
|
||||
{ on = "N", run = "find_arrow --previous", desc = "Go to the previous found" },
|
||||
|
||||
# Sorting
|
||||
{ on = [ ",", "m" ], run = "sort modified --dir-first", desc = "Sort by modified time" },
|
||||
{ on = [ ",", "M" ], run = "sort modified --reverse --dir-first", desc = "Sort by modified time (reverse)" },
|
||||
{ on = [ ",", "c" ], run = "sort created --dir-first", desc = "Sort by created time" },
|
||||
{ on = [ ",", "C" ], run = "sort created --reverse --dir-first", desc = "Sort by created time (reverse)" },
|
||||
{ on = [ ",", "e" ], run = "sort extension --dir-first", desc = "Sort by extension" },
|
||||
{ on = [ ",", "E" ], run = "sort extension --reverse --dir-first", desc = "Sort by extension (reverse)" },
|
||||
{ on = [ ",", "a" ], run = "sort alphabetical --dir-first", desc = "Sort alphabetically" },
|
||||
{ on = [ ",", "A" ], run = "sort alphabetical --reverse --dir-first", desc = "Sort alphabetically (reverse)" },
|
||||
{ on = [ ",", "n" ], run = "sort natural --dir-first", desc = "Sort naturally" },
|
||||
{ on = [ ",", "N" ], run = "sort natural --reverse --dir-first", desc = "Sort naturally (reverse)" },
|
||||
{ on = [ ",", "s" ], run = "sort size --dir-first", desc = "Sort by size" },
|
||||
{ on = [ ",", "S" ], run = "sort size --reverse --dir-first", desc = "Sort by size (reverse)" },
|
||||
{ on = [ ",", "m" ], run = "sort modified --reverse=no", desc = "Sort by modified time" },
|
||||
{ on = [ ",", "M" ], run = "sort modified --reverse", desc = "Sort by modified time (reverse)" },
|
||||
{ on = [ ",", "c" ], run = "sort created --reverse=no", desc = "Sort by created time" },
|
||||
{ on = [ ",", "C" ], run = "sort created --reverse", desc = "Sort by created time (reverse)" },
|
||||
{ on = [ ",", "e" ], run = "sort extension --reverse=no", desc = "Sort by extension" },
|
||||
{ on = [ ",", "E" ], run = "sort extension --reverse", desc = "Sort by extension (reverse)" },
|
||||
{ on = [ ",", "a" ], run = "sort alphabetical --reverse=no", desc = "Sort alphabetically" },
|
||||
{ on = [ ",", "A" ], run = "sort alphabetical --reverse", desc = "Sort alphabetically (reverse)" },
|
||||
{ on = [ ",", "n" ], run = "sort natural --reverse=no", desc = "Sort naturally" },
|
||||
{ on = [ ",", "N" ], run = "sort natural --reverse", desc = "Sort naturally (reverse)" },
|
||||
{ on = [ ",", "s" ], run = "sort size --reverse=no", desc = "Sort by size" },
|
||||
{ on = [ ",", "S" ], run = "sort size --reverse", desc = "Sort by size (reverse)" },
|
||||
|
||||
# Tabs
|
||||
{ on = [ "t" ], run = "tab_create --current", desc = "Create a new tab using the current path" },
|
||||
{ on = "t", run = "tab_create --current", desc = "Create a new tab with CWD" },
|
||||
|
||||
{ on = [ "1" ], run = "tab_switch 0", desc = "Switch to the first tab" },
|
||||
{ on = [ "2" ], run = "tab_switch 1", desc = "Switch to the second tab" },
|
||||
{ on = [ "3" ], run = "tab_switch 2", desc = "Switch to the third tab" },
|
||||
{ on = [ "4" ], run = "tab_switch 3", desc = "Switch to the fourth tab" },
|
||||
{ on = [ "5" ], run = "tab_switch 4", desc = "Switch to the fifth tab" },
|
||||
{ on = [ "6" ], run = "tab_switch 5", desc = "Switch to the sixth tab" },
|
||||
{ on = [ "7" ], run = "tab_switch 6", desc = "Switch to the seventh tab" },
|
||||
{ on = [ "8" ], run = "tab_switch 7", desc = "Switch to the eighth tab" },
|
||||
{ on = [ "9" ], run = "tab_switch 8", desc = "Switch to the ninth tab" },
|
||||
{ on = "1", run = "tab_switch 0", desc = "Switch to the first tab" },
|
||||
{ on = "2", run = "tab_switch 1", desc = "Switch to the second tab" },
|
||||
{ on = "3", run = "tab_switch 2", desc = "Switch to the third tab" },
|
||||
{ on = "4", run = "tab_switch 3", desc = "Switch to the fourth tab" },
|
||||
{ on = "5", run = "tab_switch 4", desc = "Switch to the fifth tab" },
|
||||
{ on = "6", run = "tab_switch 5", desc = "Switch to the sixth tab" },
|
||||
{ on = "7", run = "tab_switch 6", desc = "Switch to the seventh tab" },
|
||||
{ on = "8", run = "tab_switch 7", desc = "Switch to the eighth tab" },
|
||||
{ on = "9", run = "tab_switch 8", desc = "Switch to the ninth tab" },
|
||||
|
||||
{ on = [ "[" ], run = "tab_switch -1 --relative", desc = "Switch to the previous tab" },
|
||||
{ on = [ "]" ], run = "tab_switch 1 --relative", desc = "Switch to the next tab" },
|
||||
{ on = "[", run = "tab_switch -1 --relative", desc = "Switch to the previous tab" },
|
||||
{ on = "]", run = "tab_switch 1 --relative", desc = "Switch to the next tab" },
|
||||
|
||||
{ on = [ "{" ], run = "tab_swap -1", desc = "Swap the current tab with the previous tab" },
|
||||
{ on = [ "}" ], run = "tab_swap 1", desc = "Swap the current tab with the next tab" },
|
||||
{ on = "{", run = "tab_swap -1", desc = "Swap current tab with previous tab" },
|
||||
{ on = "}", run = "tab_swap 1", desc = "Swap current tab with next tab" },
|
||||
|
||||
# Tasks
|
||||
{ on = [ "w" ], run = "tasks_show", desc = "Show the tasks manager" },
|
||||
{ on = "w", run = "tasks_show", desc = "Show task manager" },
|
||||
|
||||
# Goto
|
||||
{ on = [ "g", "h" ], run = "cd ~", desc = "Go to the home directory" },
|
||||
{ on = [ "g", "c" ], run = "cd ~/.config", desc = "Go to the config directory" },
|
||||
{ on = [ "g", "d" ], run = "cd ~/Downloads", desc = "Go to the downloads directory" },
|
||||
{ on = [ "g", "t" ], run = "cd /tmp", desc = "Go to the temporary directory" },
|
||||
{ on = [ "g", "<Space>" ], run = "cd --interactive", desc = "Go to a directory interactively" },
|
||||
|
||||
# Help
|
||||
{ on = [ "~" ], run = "help", desc = "Open help" },
|
||||
{ on = "~", run = "help", desc = "Open help" },
|
||||
]
|
||||
|
||||
[tasks]
|
||||
|
||||
keymap = [
|
||||
{ on = [ "<Esc>" ], run = "close", desc = "Hide the task manager" },
|
||||
{ on = [ "<C-[>" ], run = "close", desc = "Hide the task manager" },
|
||||
{ on = [ "<C-q>" ], run = "close", desc = "Hide the task manager" },
|
||||
{ on = [ "w" ], run = "close", desc = "Hide the task manager" },
|
||||
{ on = "<Esc>", run = "close", desc = "Close task manager" },
|
||||
{ on = "<C-[>", run = "close", desc = "Close task manager" },
|
||||
{ on = "<C-c>", run = "close", desc = "Close task manager" },
|
||||
{ on = "w", run = "close", desc = "Close task manager" },
|
||||
|
||||
{ on = [ "k" ], run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = [ "j" ], run = "arrow 1", desc = "Move cursor down" },
|
||||
{ on = "k", run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = "j", run = "arrow 1", desc = "Move cursor down" },
|
||||
|
||||
{ on = [ "<Up>" ], run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = [ "<Down>" ], run = "arrow 1", desc = "Move cursor down" },
|
||||
{ on = "<Up>", run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = "<Down>", run = "arrow 1", desc = "Move cursor down" },
|
||||
|
||||
{ on = [ "<Enter>" ], run = "inspect", desc = "Inspect the task" },
|
||||
{ on = [ "x" ], run = "cancel", desc = "Cancel the task" },
|
||||
{ on = "<Enter>", run = "inspect", desc = "Inspect the task" },
|
||||
{ on = "x", run = "cancel", desc = "Cancel the task" },
|
||||
|
||||
{ on = [ "~" ], run = "help", desc = "Open help" }
|
||||
{ on = "~", run = "help", desc = "Open help" }
|
||||
]
|
||||
|
||||
[select]
|
||||
|
||||
keymap = [
|
||||
{ on = [ "<Esc>" ], run = "close", desc = "Cancel selection" },
|
||||
{ on = [ "<C-[>" ], run = "close", desc = "Cancel selection" },
|
||||
{ on = [ "<C-q>" ], run = "close", desc = "Cancel selection" },
|
||||
{ on = [ "<Enter>" ], run = "close --submit", desc = "Submit the selection" },
|
||||
{ on = "<Esc>", run = "close", desc = "Cancel selection" },
|
||||
{ on = "<C-[>", run = "close", desc = "Cancel selection" },
|
||||
{ on = "<C-c>", run = "close", desc = "Cancel selection" },
|
||||
{ on = "<Enter>", run = "close --submit", desc = "Submit the selection" },
|
||||
|
||||
{ on = [ "k" ], run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = [ "j" ], run = "arrow 1", desc = "Move cursor down" },
|
||||
{ on = "k", run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = "j", run = "arrow 1", desc = "Move cursor down" },
|
||||
|
||||
{ on = [ "K" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
|
||||
{ on = [ "J" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
|
||||
{ on = "K", run = "arrow -5", desc = "Move cursor up 5 lines" },
|
||||
{ on = "J", run = "arrow 5", desc = "Move cursor down 5 lines" },
|
||||
|
||||
{ on = [ "<Up>" ], run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = [ "<Down>" ], run = "arrow 1", desc = "Move cursor down" },
|
||||
{ on = "<Up>", run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = "<Down>", run = "arrow 1", desc = "Move cursor down" },
|
||||
|
||||
{ on = [ "<S-Up>" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
|
||||
{ on = [ "<S-Down>" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
|
||||
{ on = "<S-Up>", run = "arrow -5", desc = "Move cursor up 5 lines" },
|
||||
{ on = "<S-Down>", run = "arrow 5", desc = "Move cursor down 5 lines" },
|
||||
|
||||
{ on = [ "~" ], run = "help", desc = "Open help" }
|
||||
{ on = "~", run = "help", desc = "Open help" }
|
||||
]
|
||||
|
||||
[input]
|
||||
|
||||
keymap = [
|
||||
{ on = [ "<C-q>" ], run = "close", desc = "Cancel input" },
|
||||
{ on = [ "<Enter>" ], run = "close --submit", desc = "Submit the input" },
|
||||
{ on = [ "<Esc>" ], run = "escape", desc = "Go back the normal mode, or cancel input" },
|
||||
{ on = [ "<C-[>" ], run = "escape", desc = "Go back the normal mode, or cancel input" },
|
||||
{ on = "<C-c>", run = "close", desc = "Cancel input" },
|
||||
{ on = "<Enter>", run = "close --submit", desc = "Submit the input" },
|
||||
{ on = "<Esc>", run = "escape", desc = "Go back the normal mode, or cancel input" },
|
||||
{ on = "<C-[>", run = "escape", desc = "Go back the normal mode, or cancel input" },
|
||||
|
||||
# Mode
|
||||
{ on = [ "i" ], run = "insert", desc = "Enter insert mode" },
|
||||
{ on = [ "a" ], run = "insert --append", desc = "Enter append mode" },
|
||||
{ on = [ "I" ], run = [ "move -999", "insert" ], desc = "Move to the BOL, and enter insert mode" },
|
||||
{ on = [ "A" ], run = [ "move 999", "insert --append" ], desc = "Move to the EOL, and enter append mode" },
|
||||
{ on = [ "v" ], run = "visual", desc = "Enter visual mode" },
|
||||
{ on = [ "V" ], run = [ "move -999", "visual", "move 999" ], desc = "Enter visual mode and select all" },
|
||||
{ on = "i", run = "insert", desc = "Enter insert mode" },
|
||||
{ on = "a", run = "insert --append", desc = "Enter append mode" },
|
||||
{ on = "I", run = [ "move -999", "insert" ], desc = "Move to the BOL, and enter insert mode" },
|
||||
{ on = "A", run = [ "move 999", "insert --append" ], desc = "Move to the EOL, and enter append mode" },
|
||||
{ on = "v", run = "visual", desc = "Enter visual mode" },
|
||||
{ on = "V", run = [ "move -999", "visual", "move 999" ], desc = "Enter visual mode and select all" },
|
||||
|
||||
# Character-wise movement
|
||||
{ on = [ "h" ], run = "move -1", desc = "Move back a character" },
|
||||
{ on = [ "l" ], run = "move 1", desc = "Move forward a character" },
|
||||
{ on = [ "<Left>" ], run = "move -1", desc = "Move back a character" },
|
||||
{ on = [ "<Right>" ], run = "move 1", desc = "Move forward a character" },
|
||||
{ on = [ "<C-b>" ], run = "move -1", desc = "Move back a character" },
|
||||
{ on = [ "<C-f>" ], run = "move 1", desc = "Move forward a character" },
|
||||
{ on = "h", run = "move -1", desc = "Move back a character" },
|
||||
{ on = "l", run = "move 1", desc = "Move forward a character" },
|
||||
{ on = "<Left>", run = "move -1", desc = "Move back a character" },
|
||||
{ on = "<Right>", run = "move 1", desc = "Move forward a character" },
|
||||
{ on = "<C-b>", run = "move -1", desc = "Move back a character" },
|
||||
{ on = "<C-f>", run = "move 1", desc = "Move forward a character" },
|
||||
|
||||
# Word-wise movement
|
||||
{ on = [ "b" ], run = "backward", desc = "Move back to the start of the current or previous word" },
|
||||
{ on = [ "w" ], run = "forward", desc = "Move forward to the start of the next word" },
|
||||
{ on = [ "e" ], run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" },
|
||||
{ on = [ "<A-b>" ], run = "backward", desc = "Move back to the start of the current or previous word" },
|
||||
{ on = [ "<A-f>" ], run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" },
|
||||
{ on = "b", run = "backward", desc = "Move back to the start of the current or previous word" },
|
||||
{ on = "w", run = "forward", desc = "Move forward to the start of the next word" },
|
||||
{ on = "e", run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" },
|
||||
{ on = "<A-b>", run = "backward", desc = "Move back to the start of the current or previous word" },
|
||||
{ on = "<A-f>", run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" },
|
||||
|
||||
# Line-wise movement
|
||||
{ on = [ "0" ], run = "move -999", desc = "Move to the BOL" },
|
||||
{ on = [ "$" ], run = "move 999", desc = "Move to the EOL" },
|
||||
{ on = [ "<C-a>" ], run = "move -999", desc = "Move to the BOL" },
|
||||
{ on = [ "<C-e>" ], run = "move 999", desc = "Move to the EOL" },
|
||||
{ on = [ "<Home>" ], run = "move -999", desc = "Move to the BOL" },
|
||||
{ on = [ "<End>" ], run = "move 999", desc = "Move to the EOL" },
|
||||
{ on = "0", run = "move -999", desc = "Move to the BOL" },
|
||||
{ on = "$", run = "move 999", desc = "Move to the EOL" },
|
||||
{ on = "<C-a>", run = "move -999", desc = "Move to the BOL" },
|
||||
{ on = "<C-e>", run = "move 999", desc = "Move to the EOL" },
|
||||
{ on = "<Home>", run = "move -999", desc = "Move to the BOL" },
|
||||
{ on = "<End>", run = "move 999", desc = "Move to the EOL" },
|
||||
|
||||
# Delete
|
||||
{ on = [ "<Backspace>" ], run = "backspace", desc = "Delete the character before the cursor" },
|
||||
{ on = [ "<Delete>" ], run = "backspace --under", desc = "Delete the character under the cursor" },
|
||||
{ on = [ "<C-h>" ], run = "backspace", desc = "Delete the character before the cursor" },
|
||||
{ on = [ "<C-d>" ], run = "backspace --under", desc = "Delete the character under the cursor" },
|
||||
{ on = "<Backspace>", run = "backspace", desc = "Delete the character before the cursor" },
|
||||
{ on = "<Delete>", run = "backspace --under", desc = "Delete the character under the cursor" },
|
||||
{ on = "<C-h>", run = "backspace", desc = "Delete the character before the cursor" },
|
||||
{ on = "<C-d>", run = "backspace --under", desc = "Delete the character under the cursor" },
|
||||
|
||||
# Kill
|
||||
{ on = [ "<C-u>" ], run = "kill bol", desc = "Kill backwards to the BOL" },
|
||||
{ on = [ "<C-k>" ], run = "kill eol", desc = "Kill forwards to the EOL" },
|
||||
{ on = [ "<C-w>" ], run = "kill backward", desc = "Kill backwards to the start of the current word" },
|
||||
{ on = [ "<A-d>" ], run = "kill forward", desc = "Kill forwards to the end of the current word" },
|
||||
{ on = "<C-u>", run = "kill bol", desc = "Kill backwards to the BOL" },
|
||||
{ on = "<C-k>", run = "kill eol", desc = "Kill forwards to the EOL" },
|
||||
{ on = "<C-w>", run = "kill backward", desc = "Kill backwards to the start of the current word" },
|
||||
{ on = "<A-d>", run = "kill forward", desc = "Kill forwards to the end of the current word" },
|
||||
|
||||
# Cut/Yank/Paste
|
||||
{ on = [ "d" ], run = "delete --cut", desc = "Cut the selected characters" },
|
||||
{ on = [ "D" ], run = [ "delete --cut", "move 999" ], desc = "Cut until the EOL" },
|
||||
{ on = [ "c" ], run = "delete --cut --insert", desc = "Cut the selected characters, and enter insert mode" },
|
||||
{ on = [ "C" ], run = [ "delete --cut --insert", "move 999" ], desc = "Cut until the EOL, and enter insert mode" },
|
||||
{ on = [ "x" ], run = [ "delete --cut", "move 1 --in-operating" ], desc = "Cut the current character" },
|
||||
{ on = [ "y" ], run = "yank", desc = "Copy the selected characters" },
|
||||
{ on = [ "p" ], run = "paste", desc = "Paste the copied characters after the cursor" },
|
||||
{ on = [ "P" ], run = "paste --before", desc = "Paste the copied characters before the cursor" },
|
||||
{ on = "d", run = "delete --cut", desc = "Cut the selected characters" },
|
||||
{ on = "D", run = [ "delete --cut", "move 999" ], desc = "Cut until the EOL" },
|
||||
{ on = "c", run = "delete --cut --insert", desc = "Cut the selected characters, and enter insert mode" },
|
||||
{ on = "C", run = [ "delete --cut --insert", "move 999" ], desc = "Cut until the EOL, and enter insert mode" },
|
||||
{ on = "x", run = [ "delete --cut", "move 1 --in-operating" ], desc = "Cut the current character" },
|
||||
{ on = "y", run = "yank", desc = "Copy the selected characters" },
|
||||
{ on = "p", run = "paste", desc = "Paste the copied characters after the cursor" },
|
||||
{ on = "P", run = "paste --before", desc = "Paste the copied characters before the cursor" },
|
||||
|
||||
# Undo/Redo
|
||||
{ on = [ "u" ], run = "undo", desc = "Undo the last operation" },
|
||||
{ on = [ "<C-r>" ], run = "redo", desc = "Redo the last operation" },
|
||||
{ on = "u", run = "undo", desc = "Undo the last operation" },
|
||||
{ on = "<C-r>", run = "redo", desc = "Redo the last operation" },
|
||||
|
||||
# Help
|
||||
{ on = [ "~" ], run = "help", desc = "Open help" }
|
||||
{ on = "~", run = "help", desc = "Open help" }
|
||||
]
|
||||
|
||||
[completion]
|
||||
|
||||
keymap = [
|
||||
{ on = [ "<C-q>" ], run = "close", desc = "Cancel completion" },
|
||||
{ on = [ "<Tab>" ], run = "close --submit", desc = "Submit the completion" },
|
||||
{ on = [ "<Enter>" ], run = [ "close --submit", "close_input --submit" ], desc = "Submit the completion and input" },
|
||||
{ on = "<C-c>", run = "close", desc = "Cancel completion" },
|
||||
{ on = "<Tab>", run = "close --submit", desc = "Submit the completion" },
|
||||
{ on = "<Enter>", run = [ "close --submit", "close_input --submit" ], desc = "Submit the completion and input" },
|
||||
|
||||
{ on = [ "<A-k>" ], run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = [ "<A-j>" ], run = "arrow 1", desc = "Move cursor down" },
|
||||
{ on = "<A-k>", run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = "<A-j>", run = "arrow 1", desc = "Move cursor down" },
|
||||
|
||||
{ on = [ "<Up>" ], run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = [ "<Down>" ], run = "arrow 1", desc = "Move cursor down" },
|
||||
{ on = "<Up>", run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = "<Down>", run = "arrow 1", desc = "Move cursor down" },
|
||||
|
||||
{ on = [ "<C-p>" ], run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = [ "<C-n>" ], run = "arrow 1", desc = "Move cursor down" },
|
||||
{ on = "<C-p>", run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = "<C-n>", run = "arrow 1", desc = "Move cursor down" },
|
||||
|
||||
{ on = [ "~" ], run = "help", desc = "Open help" }
|
||||
{ on = "~", run = "help", desc = "Open help" }
|
||||
]
|
||||
|
||||
[help]
|
||||
|
||||
keymap = [
|
||||
{ on = [ "<Esc>" ], run = "escape", desc = "Clear the filter, or hide the help" },
|
||||
{ on = [ "<C-[>" ], run = "escape", desc = "Clear the filter, or hide the help" },
|
||||
{ on = [ "q" ], run = "close", desc = "Exit the process" },
|
||||
{ on = [ "<C-q>" ], run = "close", desc = "Hide the help" },
|
||||
{ on = "<Esc>", run = "escape", desc = "Clear the filter, or hide the help" },
|
||||
{ on = "<C-[>", run = "escape", desc = "Clear the filter, or hide the help" },
|
||||
{ on = "q", run = "close", desc = "Exit the process" },
|
||||
{ on = "<C-c>", run = "close", desc = "Hide the help" },
|
||||
|
||||
# Navigation
|
||||
{ on = [ "k" ], run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = [ "j" ], run = "arrow 1", desc = "Move cursor down" },
|
||||
{ on = "k", run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = "j", run = "arrow 1", desc = "Move cursor down" },
|
||||
|
||||
{ on = [ "K" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
|
||||
{ on = [ "J" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
|
||||
{ on = "K", run = "arrow -5", desc = "Move cursor up 5 lines" },
|
||||
{ on = "J", run = "arrow 5", desc = "Move cursor down 5 lines" },
|
||||
|
||||
{ on = [ "<Up>" ], run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = [ "<Down>" ], run = "arrow 1", desc = "Move cursor down" },
|
||||
{ on = "<Up>", run = "arrow -1", desc = "Move cursor up" },
|
||||
{ on = "<Down>", run = "arrow 1", desc = "Move cursor down" },
|
||||
|
||||
{ on = [ "<S-Up>" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
|
||||
{ on = [ "<S-Down>" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
|
||||
{ on = "<S-Up>", run = "arrow -5", desc = "Move cursor up 5 lines" },
|
||||
{ on = "<S-Down>", run = "arrow 5", desc = "Move cursor down 5 lines" },
|
||||
|
||||
# Filtering
|
||||
{ on = [ "/" ], run = "filter", desc = "Apply a filter for the help items" },
|
||||
{ on = "/", run = "filter", desc = "Apply a filter for the help items" },
|
||||
]
|
||||
|
|
|
|||
|
|
@ -170,17 +170,24 @@ icon_error = ""
|
|||
|
||||
rules = [
|
||||
# Images
|
||||
{ mime = "image/*", fg = "cyan" },
|
||||
{ mime = "image/*", fg = "yellow" },
|
||||
|
||||
# Media
|
||||
{ mime = "{audio,video}/*", fg = "yellow" },
|
||||
{ mime = "{audio,video}/*", fg = "magenta" },
|
||||
|
||||
# Archives
|
||||
{ mime = "application/*zip", fg = "magenta" },
|
||||
{ mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", fg = "magenta" },
|
||||
{ mime = "application/{,g}zip", fg = "red" },
|
||||
{ mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", fg = "red" },
|
||||
|
||||
# Documents
|
||||
{ mime = "application/{pdf,doc,rtf,vnd.*}", fg = "green" },
|
||||
{ mime = "application/{pdf,doc,rtf,vnd.*}", fg = "cyan" },
|
||||
|
||||
# Empty files
|
||||
# { mime = "inode/x-empty", fg = "red" },
|
||||
|
||||
# Special files
|
||||
{ name = "*", is = "orphan", bg = "red" },
|
||||
{ name = "*", is = "exec" , fg = "green" },
|
||||
|
||||
# Fallback
|
||||
# { name = "*", fg = "white" },
|
||||
|
|
@ -189,155 +196,586 @@ rules = [
|
|||
|
||||
[icon]
|
||||
|
||||
rules = [
|
||||
# Programming
|
||||
{ name = "*.c" , text = "", fg = "#599eff" },
|
||||
{ name = "*.cpp" , text = "", fg = "#519aba" },
|
||||
{ name = "*.class", text = "", fg = "#cc3e44" },
|
||||
{ name = "*.cs" , text = "", fg = "#596706" },
|
||||
{ name = "*.css" , text = "", fg = "#42a5f5" },
|
||||
{ name = "*.elm" , text = "", fg = "#4391d2" },
|
||||
{ name = "*.fish" , text = "", fg = "#4d5a5e" },
|
||||
{ name = "*.go" , text = "", fg = "#519aba" },
|
||||
{ name = "*.h" , text = "", fg = "#a074c4" },
|
||||
{ name = "*.hpp" , text = "", fg = "#a074c4" },
|
||||
{ name = "*.html" , text = "", fg = "#e44d26" },
|
||||
{ name = "*.jar" , text = "", fg = "#cc3e44" },
|
||||
{ name = "*.java" , text = "", fg = "#cc3e44" },
|
||||
{ name = "*.js" , text = "", fg = "#F1F134" },
|
||||
{ name = "*.jsx" , text = "", fg = "#20c2e3" },
|
||||
{ name = "*.lua" , text = "", fg = "#51a0cf" },
|
||||
{ name = "*.nix" , text = "", fg = "#7ebae4" },
|
||||
{ name = "*.nu" , text = ">", fg = "#3aa675" },
|
||||
{ name = "*.php" , text = "", fg = "#a074c4" },
|
||||
{ name = "*.py" , text = "", fg = "#ffbc03" },
|
||||
{ name = "*.rb" , text = "", fg = "#701516" },
|
||||
{ name = "*.rs" , text = "", fg = "#dea584" },
|
||||
{ name = "*.sbt" , text = "", fg = "#4d5a5e" },
|
||||
{ name = "*.scala", text = "", fg = "#cc463e" },
|
||||
{ name = "*.scss" , text = "", fg = "#f55385" },
|
||||
{ name = "*.sh" , text = "", fg = "#4d5a5e" },
|
||||
{ name = "*.swift", text = "", fg = "#e37933" },
|
||||
{ name = "*.ts" , text = "", fg = "#519aba" },
|
||||
{ name = "*.tsx" , text = "", fg = "#1354bf" },
|
||||
{ name = "*.vim" , text = "", fg = "#019833" },
|
||||
{ name = "*.vue" , text = "", fg = "#8dc149" },
|
||||
globs = []
|
||||
dirs = [
|
||||
{ name = ".config", text = "" },
|
||||
{ name = ".git", text = "" },
|
||||
{ name = "Desktop", text = "" },
|
||||
{ name = "Development", text = "" },
|
||||
{ name = "Documents", text = "" },
|
||||
{ name = "Downloads", text = "" },
|
||||
{ name = "Library", text = "" },
|
||||
{ name = "Movies", text = "" },
|
||||
{ name = "Music", text = "" },
|
||||
{ name = "Pictures", text = "" },
|
||||
{ name = "Public", text = "" },
|
||||
{ name = "Videos", text = "" },
|
||||
]
|
||||
files = [
|
||||
{ name = ".babelrc", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = ".bash_profile", text = "", fg_dark = "#89e051", fg_light = "#447028" },
|
||||
{ name = ".bashrc", text = "", fg_dark = "#89e051", fg_light = "#447028" },
|
||||
{ name = ".dockerignore", text = "", fg_dark = "#458ee6", fg_light = "#2e5f99" },
|
||||
{ name = ".ds_store", text = "", fg_dark = "#41535b", fg_light = "#41535b" },
|
||||
{ name = ".editorconfig", text = "", fg_dark = "#fff2f2", fg_light = "#333030" },
|
||||
{ name = ".env", text = "", fg_dark = "#faf743", fg_light = "#32310d" },
|
||||
{ name = ".eslintignore", text = "", fg_dark = "#4b32c3", fg_light = "#4b32c3" },
|
||||
{ name = ".eslintrc", text = "", fg_dark = "#4b32c3", fg_light = "#4b32c3" },
|
||||
{ name = ".gitattributes", text = "", fg_dark = "#f54d27", fg_light = "#b83a1d" },
|
||||
{ name = ".gitconfig", text = "", fg_dark = "#f54d27", fg_light = "#b83a1d" },
|
||||
{ name = ".gitignore", text = "", fg_dark = "#f54d27", fg_light = "#b83a1d" },
|
||||
{ name = ".gitlab-ci.yml", text = "", fg_dark = "#e24329", fg_light = "#aa321f" },
|
||||
{ name = ".gitmodules", text = "", fg_dark = "#f54d27", fg_light = "#b83a1d" },
|
||||
{ name = ".gtkrc-2.0", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = ".gvimrc", text = "", fg_dark = "#019833", fg_light = "#017226" },
|
||||
{ name = ".luaurc", text = "", fg_dark = "#00a2ff", fg_light = "#007abf" },
|
||||
{ name = ".mailmap", text = "", fg_dark = "#41535b", fg_light = "#41535b" },
|
||||
{ name = ".npmignore", text = "", fg_dark = "#e8274b", fg_light = "#ae1d38" },
|
||||
{ name = ".npmrc", text = "", fg_dark = "#e8274b", fg_light = "#ae1d38" },
|
||||
{ name = ".prettierrc", text = "", fg_dark = "#4285f4", fg_light = "#3264b7" },
|
||||
{ name = ".settings.json", text = "", fg_dark = "#854cc7", fg_light = "#643995" },
|
||||
{ name = ".SRCINFO", text = "", fg_dark = "#0f94d2", fg_light = "#0b6f9e" },
|
||||
{ name = ".vimrc", text = "", fg_dark = "#019833", fg_light = "#017226" },
|
||||
{ name = ".Xauthority", text = "", fg_dark = "#e54d18", fg_light = "#ac3a12" },
|
||||
{ name = ".xinitrc", text = "", fg_dark = "#e54d18", fg_light = "#ac3a12" },
|
||||
{ name = ".Xresources", text = "", fg_dark = "#e54d18", fg_light = "#ac3a12" },
|
||||
{ name = ".xsession", text = "", fg_dark = "#e54d18", fg_light = "#ac3a12" },
|
||||
{ name = ".zprofile", text = "", fg_dark = "#89e051", fg_light = "#447028" },
|
||||
{ name = ".zshenv", text = "", fg_dark = "#89e051", fg_light = "#447028" },
|
||||
{ name = ".zshrc", text = "", fg_dark = "#89e051", fg_light = "#447028" },
|
||||
{ name = "_gvimrc", text = "", fg_dark = "#019833", fg_light = "#017226" },
|
||||
{ name = "_vimrc", text = "", fg_dark = "#019833", fg_light = "#017226" },
|
||||
{ name = "avif", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "brewfile", text = "", fg_dark = "#701516", fg_light = "#701516" },
|
||||
{ name = "bspwmrc", text = "", fg_dark = "#2f2f2f", fg_light = "#2f2f2f" },
|
||||
{ name = "build", text = "", fg_dark = "#89e051", fg_light = "#447028" },
|
||||
{ name = "build.gradle", text = "", fg_dark = "#005f87", fg_light = "#005f87" },
|
||||
{ name = "build.zig.zon", text = "", fg_dark = "#f69a1b", fg_light = "#7b4d0e" },
|
||||
{ name = "cantorrc", text = "", fg_dark = "#1c99f3", fg_light = "#1573b6" },
|
||||
{ name = "checkhealth", text = "", fg_dark = "#75b4fb", fg_light = "#3a5a7e" },
|
||||
{ name = "cmakelists.txt", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "commit_editmsg", text = "", fg_dark = "#f54d27", fg_light = "#b83a1d" },
|
||||
{ name = "compose.yaml", text = "", fg_dark = "#458ee6", fg_light = "#2e5f99" },
|
||||
{ name = "compose.yml", text = "", fg_dark = "#458ee6", fg_light = "#2e5f99" },
|
||||
{ name = "config", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "containerfile", text = "", fg_dark = "#458ee6", fg_light = "#2e5f99" },
|
||||
{ name = "copying", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "copying.lesser", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "docker-compose.yaml", text = "", fg_dark = "#458ee6", fg_light = "#2e5f99" },
|
||||
{ name = "docker-compose.yml", text = "", fg_dark = "#458ee6", fg_light = "#2e5f99" },
|
||||
{ name = "dockerfile", text = "", fg_dark = "#458ee6", fg_light = "#2e5f99" },
|
||||
{ name = "ext_typoscript_setup.txt", text = "", fg_dark = "#ff8700", fg_light = "#aa5a00" },
|
||||
{ name = "favicon.ico", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "fp-info-cache", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = "fp-lib-table", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = "FreeCAD.conf", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" },
|
||||
{ name = "gemfile$", text = "", fg_dark = "#701516", fg_light = "#701516" },
|
||||
{ name = "gnumakefile", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "gradle-wrapper.properties", text = "", fg_dark = "#005f87", fg_light = "#005f87" },
|
||||
{ name = "gradle.properties", text = "", fg_dark = "#005f87", fg_light = "#005f87" },
|
||||
{ name = "gradlew", text = "", fg_dark = "#005f87", fg_light = "#005f87" },
|
||||
{ name = "groovy", text = "", fg_dark = "#4a687c", fg_light = "#384e5d" },
|
||||
{ name = "gruntfile.babel.js", text = "", fg_dark = "#e37933", fg_light = "#975122" },
|
||||
{ name = "gruntfile.coffee", text = "", fg_dark = "#e37933", fg_light = "#975122" },
|
||||
{ name = "gruntfile.js", text = "", fg_dark = "#e37933", fg_light = "#975122" },
|
||||
{ name = "gruntfile.ts", text = "", fg_dark = "#e37933", fg_light = "#975122" },
|
||||
{ name = "gtkrc", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = "gulpfile.babel.js", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" },
|
||||
{ name = "gulpfile.coffee", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" },
|
||||
{ name = "gulpfile.js", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" },
|
||||
{ name = "gulpfile.ts", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" },
|
||||
{ name = "hyprland.conf", text = "", fg_dark = "#00aaae", fg_light = "#008082" },
|
||||
{ name = "i3blocks.conf", text = "", fg_dark = "#e8ebee", fg_light = "#2e2f30" },
|
||||
{ name = "i3status.conf", text = "", fg_dark = "#e8ebee", fg_light = "#2e2f30" },
|
||||
{ name = "kalgebrarc", text = "", fg_dark = "#1c99f3", fg_light = "#1573b6" },
|
||||
{ name = "kdeglobals", text = "", fg_dark = "#1c99f3", fg_light = "#1573b6" },
|
||||
{ name = "kdenlive-layoutsrc", text = "", fg_dark = "#83b8f2", fg_light = "#425c79" },
|
||||
{ name = "kdenliverc", text = "", fg_dark = "#83b8f2", fg_light = "#425c79" },
|
||||
{ name = "kritadisplayrc", text = "", fg_dark = "#f245fb", fg_light = "#a12ea7" },
|
||||
{ name = "kritarc", text = "", fg_dark = "#f245fb", fg_light = "#a12ea7" },
|
||||
{ name = "license", text = "", fg_dark = "#d0bf41", fg_light = "#686020" },
|
||||
{ name = "lxde-rc.xml", text = "", fg_dark = "#909090", fg_light = "#606060" },
|
||||
{ name = "lxqt.conf", text = "", fg_dark = "#0192d3", fg_light = "#016e9e" },
|
||||
{ name = "makefile", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "mix.lock", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "mpv.conf", text = "", fg_dark = "#3b1342", fg_light = "#3b1342" },
|
||||
{ name = "node_modules", text = "", fg_dark = "#e8274b", fg_light = "#ae1d38" },
|
||||
{ name = "package-lock.json", text = "", fg_dark = "#7a0d21", fg_light = "#7a0d21" },
|
||||
{ name = "package.json", text = "", fg_dark = "#e8274b", fg_light = "#ae1d38" },
|
||||
{ name = "PKGBUILD", text = "", fg_dark = "#0f94d2", fg_light = "#0b6f9e" },
|
||||
{ name = "platformio.ini", text = "", fg_dark = "#f6822b", fg_light = "#a4571d" },
|
||||
{ name = "pom.xml", text = "", fg_dark = "#7a0d21", fg_light = "#7a0d21" },
|
||||
{ name = "procfile", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "PrusaSlicer.ini", text = "", fg_dark = "#ec6b23", fg_light = "#9d4717" },
|
||||
{ name = "PrusaSlicerGcodeViewer.ini", text = "", fg_dark = "#ec6b23", fg_light = "#9d4717" },
|
||||
{ name = "py.typed", text = "", fg_dark = "#ffbc03", fg_light = "#805e02" },
|
||||
{ name = "QtProject.conf", text = "", fg_dark = "#40cd52", fg_light = "#2b8937" },
|
||||
{ name = "R", text = "", fg_dark = "#2266ba", fg_light = "#1a4c8c" },
|
||||
{ name = "r", text = "", fg_dark = "#2266ba", fg_light = "#1a4c8c" },
|
||||
{ name = "rakefile", text = "", fg_dark = "#701516", fg_light = "#701516" },
|
||||
{ name = "rmd", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "settings.gradle", text = "", fg_dark = "#005f87", fg_light = "#005f87" },
|
||||
{ name = "svelte.config.js", text = "", fg_dark = "#ff3e00", fg_light = "#bf2e00" },
|
||||
{ name = "sxhkdrc", text = "", fg_dark = "#2f2f2f", fg_light = "#2f2f2f" },
|
||||
{ name = "sym-lib-table", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = "tailwind.config.js", text = "", fg_dark = "#20c2e3", fg_light = "#158197" },
|
||||
{ name = "tailwind.config.mjs", text = "", fg_dark = "#20c2e3", fg_light = "#158197" },
|
||||
{ name = "tailwind.config.ts", text = "", fg_dark = "#20c2e3", fg_light = "#158197" },
|
||||
{ name = "tmux.conf", text = "", fg_dark = "#14ba19", fg_light = "#0f8c13" },
|
||||
{ name = "tmux.conf.local", text = "", fg_dark = "#14ba19", fg_light = "#0f8c13" },
|
||||
{ name = "tsconfig.json", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "unlicense", text = "", fg_dark = "#d0bf41", fg_light = "#686020" },
|
||||
{ name = "vagrantfile$", text = "", fg_dark = "#1563ff", fg_light = "#104abf" },
|
||||
{ name = "vlcrc", text = "", fg_dark = "#ee7a00", fg_light = "#9f5100" },
|
||||
{ name = "webpack", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "weston.ini", text = "", fg_dark = "#ffbb01", fg_light = "#805e00" },
|
||||
{ name = "workspace", text = "", fg_dark = "#89e051", fg_light = "#447028" },
|
||||
{ name = "xmobarrc", text = "", fg_dark = "#fd4d5d", fg_light = "#a9333e" },
|
||||
{ name = "xmobarrc.hs", text = "", fg_dark = "#fd4d5d", fg_light = "#a9333e" },
|
||||
{ name = "xmonad.hs", text = "", fg_dark = "#fd4d5d", fg_light = "#a9333e" },
|
||||
{ name = "xorg.conf", text = "", fg_dark = "#e54d18", fg_light = "#ac3a12" },
|
||||
{ name = "xsettingsd.conf", text = "", fg_dark = "#e54d18", fg_light = "#ac3a12" },
|
||||
]
|
||||
exts = [
|
||||
{ name = "3gp", text = "", fg_dark = "#fd971f", fg_light = "#7e4c10" },
|
||||
{ name = "3mf", text = "", fg_dark = "#888888", fg_light = "#5b5b5b" },
|
||||
{ name = "7z", text = "", fg_dark = "#eca517", fg_light = "#76520c" },
|
||||
{ name = "a", text = "", fg_dark = "#dcddd6", fg_light = "#494a47" },
|
||||
{ name = "aac", text = "", fg_dark = "#00afff", fg_light = "#0075aa" },
|
||||
{ name = "ai", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "aif", text = "", fg_dark = "#00afff", fg_light = "#0075aa" },
|
||||
{ name = "aiff", text = "", fg_dark = "#00afff", fg_light = "#0075aa" },
|
||||
{ name = "android", text = "", fg_dark = "#34a853", fg_light = "#277e3e" },
|
||||
{ name = "ape", text = "", fg_dark = "#00afff", fg_light = "#0075aa" },
|
||||
{ name = "apk", text = "", fg_dark = "#34a853", fg_light = "#277e3e" },
|
||||
{ name = "app", text = "", fg_dark = "#9f0500", fg_light = "#9f0500" },
|
||||
{ name = "applescript", text = "", fg_dark = "#6d8085", fg_light = "#526064" },
|
||||
{ name = "asc", text = "", fg_dark = "#576d7f", fg_light = "#41525f" },
|
||||
{ name = "ass", text = "", fg_dark = "#ffb713", fg_light = "#805c0a" },
|
||||
{ name = "astro", text = "", fg_dark = "#e23f67", fg_light = "#aa2f4d" },
|
||||
{ name = "awk", text = "", fg_dark = "#4d5a5e", fg_light = "#3a4446" },
|
||||
{ name = "azcli", text = "", fg_dark = "#0078d4", fg_light = "#005a9f" },
|
||||
{ name = "bak", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "bash", text = "", fg_dark = "#89e051", fg_light = "#447028" },
|
||||
{ name = "bat", text = "", fg_dark = "#c1f12e", fg_light = "#40500f" },
|
||||
{ name = "bazel", text = "", fg_dark = "#89e051", fg_light = "#447028" },
|
||||
{ name = "bib", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "bicep", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "bicepparam", text = "", fg_dark = "#9f74b3", fg_light = "#6a4d77" },
|
||||
{ name = "bin", text = "", fg_dark = "#9f0500", fg_light = "#9f0500" },
|
||||
{ name = "blade.php", text = "", fg_dark = "#f05340", fg_light = "#a0372b" },
|
||||
{ name = "blend", text = "", fg_dark = "#ea7600", fg_light = "#9c4f00" },
|
||||
{ name = "blp", text = "", fg_dark = "#5796e2", fg_light = "#3a6497" },
|
||||
{ name = "bmp", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "brep", text = "", fg_dark = "#839463", fg_light = "#576342" },
|
||||
{ name = "bz", text = "", fg_dark = "#eca517", fg_light = "#76520c" },
|
||||
{ name = "bz2", text = "", fg_dark = "#eca517", fg_light = "#76520c" },
|
||||
{ name = "bz3", text = "", fg_dark = "#eca517", fg_light = "#76520c" },
|
||||
{ name = "bzl", text = "", fg_dark = "#89e051", fg_light = "#447028" },
|
||||
{ name = "c", text = "", fg_dark = "#599eff", fg_light = "#3b69aa" },
|
||||
{ name = "c++", text = "", fg_dark = "#f34b7d", fg_light = "#a23253" },
|
||||
{ name = "cache", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = "cast", text = "", fg_dark = "#fd971f", fg_light = "#7e4c10" },
|
||||
{ name = "cbl", text = "⚙", fg_dark = "#005ca5", fg_light = "#005ca5" },
|
||||
{ name = "cc", text = "", fg_dark = "#f34b7d", fg_light = "#a23253" },
|
||||
{ name = "ccm", text = "", fg_dark = "#f34b7d", fg_light = "#a23253" },
|
||||
{ name = "cfg", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "cjs", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "clj", text = "", fg_dark = "#8dc149", fg_light = "#466024" },
|
||||
{ name = "cljc", text = "", fg_dark = "#8dc149", fg_light = "#466024" },
|
||||
{ name = "cljd", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "cljs", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "cmake", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "cob", text = "⚙", fg_dark = "#005ca5", fg_light = "#005ca5" },
|
||||
{ name = "cobol", text = "⚙", fg_dark = "#005ca5", fg_light = "#005ca5" },
|
||||
{ name = "coffee", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "conf", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "config.ru", text = "", fg_dark = "#701516", fg_light = "#701516" },
|
||||
{ name = "cp", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "cpp", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "cppm", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "cpy", text = "⚙", fg_dark = "#005ca5", fg_light = "#005ca5" },
|
||||
{ name = "cr", text = "", fg_dark = "#c8c8c8", fg_light = "#434343" },
|
||||
{ name = "crdownload", text = "", fg_dark = "#44cda8", fg_light = "#226654" },
|
||||
{ name = "cs", text = "", fg_dark = "#596706", fg_light = "#434d04" },
|
||||
{ name = "csh", text = "", fg_dark = "#4d5a5e", fg_light = "#3a4446" },
|
||||
{ name = "cshtml", text = "", fg_dark = "#512bd4", fg_light = "#512bd4" },
|
||||
{ name = "cson", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "csproj", text = "", fg_dark = "#512bd4", fg_light = "#512bd4" },
|
||||
{ name = "css", text = "", fg_dark = "#42a5f5", fg_light = "#2c6ea3" },
|
||||
{ name = "csv", text = "", fg_dark = "#89e051", fg_light = "#447028" },
|
||||
{ name = "cts", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "cu", text = "", fg_dark = "#89e051", fg_light = "#447028" },
|
||||
{ name = "cue", text = "", fg_dark = "#ed95ae", fg_light = "#764a57" },
|
||||
{ name = "cuh", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "cxx", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "cxxm", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "d", text = "", fg_dark = "#427819", fg_light = "#325a13" },
|
||||
{ name = "d.ts", text = "", fg_dark = "#d59855", fg_light = "#6a4c2a" },
|
||||
{ name = "dart", text = "", fg_dark = "#03589c", fg_light = "#03589c" },
|
||||
{ name = "db", text = "", fg_dark = "#dad8d8", fg_light = "#494848" },
|
||||
{ name = "dconf", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = "desktop", text = "", fg_dark = "#563d7c", fg_light = "#563d7c" },
|
||||
{ name = "diff", text = "", fg_dark = "#41535b", fg_light = "#41535b" },
|
||||
{ name = "dll", text = "", fg_dark = "#4d2c0b", fg_light = "#4d2c0b" },
|
||||
{ name = "doc", text = "", fg_dark = "#185abd", fg_light = "#185abd" },
|
||||
{ name = "Dockerfile", text = "", fg_dark = "#458ee6", fg_light = "#2e5f99" },
|
||||
{ name = "docx", text = "", fg_dark = "#185abd", fg_light = "#185abd" },
|
||||
{ name = "dot", text = "", fg_dark = "#30638e", fg_light = "#244a6a" },
|
||||
{ name = "download", text = "", fg_dark = "#44cda8", fg_light = "#226654" },
|
||||
{ name = "drl", text = "", fg_dark = "#ffafaf", fg_light = "#553a3a" },
|
||||
{ name = "dropbox", text = "", fg_dark = "#0061fe", fg_light = "#0049be" },
|
||||
{ name = "dump", text = "", fg_dark = "#dad8d8", fg_light = "#494848" },
|
||||
{ name = "dwg", text = "", fg_dark = "#839463", fg_light = "#576342" },
|
||||
{ name = "dxf", text = "", fg_dark = "#839463", fg_light = "#576342" },
|
||||
{ name = "ebook", text = "", fg_dark = "#eab16d", fg_light = "#755836" },
|
||||
{ name = "edn", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "eex", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "ejs", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "el", text = "", fg_dark = "#8172be", fg_light = "#61568e" },
|
||||
{ name = "elc", text = "", fg_dark = "#8172be", fg_light = "#61568e" },
|
||||
{ name = "elf", text = "", fg_dark = "#9f0500", fg_light = "#9f0500" },
|
||||
{ name = "elm", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "eln", text = "", fg_dark = "#8172be", fg_light = "#61568e" },
|
||||
{ name = "env", text = "", fg_dark = "#faf743", fg_light = "#32310d" },
|
||||
{ name = "eot", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" },
|
||||
{ name = "epp", text = "", fg_dark = "#ffa61a", fg_light = "#80530d" },
|
||||
{ name = "epub", text = "", fg_dark = "#eab16d", fg_light = "#755836" },
|
||||
{ name = "erb", text = "", fg_dark = "#701516", fg_light = "#701516" },
|
||||
{ name = "erl", text = "", fg_dark = "#b83998", fg_light = "#8a2b72" },
|
||||
{ name = "ex", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "exe", text = "", fg_dark = "#9f0500", fg_light = "#9f0500" },
|
||||
{ name = "exs", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "f#", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "f3d", text = "", fg_dark = "#839463", fg_light = "#576342" },
|
||||
{ name = "f90", text = "", fg_dark = "#734f96", fg_light = "#563b70" },
|
||||
{ name = "fbx", text = "", fg_dark = "#888888", fg_light = "#5b5b5b" },
|
||||
{ name = "fcbak", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" },
|
||||
{ name = "fcmacro", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" },
|
||||
{ name = "fcmat", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" },
|
||||
{ name = "fcparam", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" },
|
||||
{ name = "fcscript", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" },
|
||||
{ name = "fcstd", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" },
|
||||
{ name = "fcstd1", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" },
|
||||
{ name = "fctb", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" },
|
||||
{ name = "fctl", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" },
|
||||
{ name = "fdmdownload", text = "", fg_dark = "#44cda8", fg_light = "#226654" },
|
||||
{ name = "fish", text = "", fg_dark = "#4d5a5e", fg_light = "#3a4446" },
|
||||
{ name = "flac", text = "", fg_dark = "#0075aa", fg_light = "#005880" },
|
||||
{ name = "flc", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" },
|
||||
{ name = "flf", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" },
|
||||
{ name = "fnl", text = "", fg_dark = "#fff3d7", fg_light = "#33312b" },
|
||||
{ name = "fs", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "fsi", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "fsscript", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "fsx", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "gcode", text = "", fg_dark = "#1471ad", fg_light = "#0f5582" },
|
||||
{ name = "gd", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "gemspec", text = "", fg_dark = "#701516", fg_light = "#701516" },
|
||||
{ name = "gif", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "git", text = "", fg_dark = "#f14c28", fg_light = "#b5391e" },
|
||||
{ name = "glb", text = "", fg_dark = "#ffb13b", fg_light = "#80581e" },
|
||||
{ name = "gnumakefile", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "go", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "godot", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "gql", text = "", fg_dark = "#e535ab", fg_light = "#ac2880" },
|
||||
{ name = "graphql", text = "", fg_dark = "#e535ab", fg_light = "#ac2880" },
|
||||
{ name = "gresource", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = "gv", text = "", fg_dark = "#30638e", fg_light = "#244a6a" },
|
||||
{ name = "gz", text = "", fg_dark = "#eca517", fg_light = "#76520c" },
|
||||
{ name = "h", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "haml", text = "", fg_dark = "#eaeae1", fg_light = "#2f2f2d" },
|
||||
{ name = "hbs", text = "", fg_dark = "#f0772b", fg_light = "#a04f1d" },
|
||||
{ name = "heex", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "hex", text = "", fg_dark = "#2e63ff", fg_light = "#224abf" },
|
||||
{ name = "hh", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "hpp", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "hrl", text = "", fg_dark = "#b83998", fg_light = "#8a2b72" },
|
||||
{ name = "hs", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "htm", text = "", fg_dark = "#e34c26", fg_light = "#aa391c" },
|
||||
{ name = "html", text = "", fg_dark = "#e44d26", fg_light = "#ab3a1c" },
|
||||
{ name = "huff", text = "", fg_dark = "#4242c7", fg_light = "#4242c7" },
|
||||
{ name = "hurl", text = "", fg_dark = "#ff0288", fg_light = "#bf0266" },
|
||||
{ name = "hx", text = "", fg_dark = "#ea8220", fg_light = "#9c5715" },
|
||||
{ name = "hxx", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "ical", text = "", fg_dark = "#2b2e83", fg_light = "#2b2e83" },
|
||||
{ name = "icalendar", text = "", fg_dark = "#2b2e83", fg_light = "#2b2e83" },
|
||||
{ name = "ico", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "ics", text = "", fg_dark = "#2b2e83", fg_light = "#2b2e83" },
|
||||
{ name = "ifb", text = "", fg_dark = "#2b2e83", fg_light = "#2b2e83" },
|
||||
{ name = "ifc", text = "", fg_dark = "#839463", fg_light = "#576342" },
|
||||
{ name = "ige", text = "", fg_dark = "#839463", fg_light = "#576342" },
|
||||
{ name = "iges", text = "", fg_dark = "#839463", fg_light = "#576342" },
|
||||
{ name = "igs", text = "", fg_dark = "#839463", fg_light = "#576342" },
|
||||
{ name = "image", text = "", fg_dark = "#d0bec8", fg_light = "#453f43" },
|
||||
{ name = "img", text = "", fg_dark = "#d0bec8", fg_light = "#453f43" },
|
||||
{ name = "import", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" },
|
||||
{ name = "info", text = "", fg_dark = "#ffffcd", fg_light = "#333329" },
|
||||
{ name = "ini", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "ino", text = "", fg_dark = "#56b6c2", fg_light = "#397981" },
|
||||
{ name = "ipynb", text = "", fg_dark = "#51a0cf", fg_light = "#366b8a" },
|
||||
{ name = "iso", text = "", fg_dark = "#d0bec8", fg_light = "#453f43" },
|
||||
{ name = "ixx", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "java", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" },
|
||||
{ name = "jl", text = "", fg_dark = "#a270ba", fg_light = "#6c4b7c" },
|
||||
{ name = "jpeg", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "jpg", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "js", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "json", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "json5", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "jsonc", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "jsx", text = "", fg_dark = "#20c2e3", fg_light = "#158197" },
|
||||
{ name = "jwmrc", text = "", fg_dark = "#0078cd", fg_light = "#005a9a" },
|
||||
{ name = "jxl", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "kbx", text = "", fg_dark = "#737672", fg_light = "#565856" },
|
||||
{ name = "kdb", text = "", fg_dark = "#529b34", fg_light = "#3e7427" },
|
||||
{ name = "kdbx", text = "", fg_dark = "#529b34", fg_light = "#3e7427" },
|
||||
{ name = "kdenlive", text = "", fg_dark = "#83b8f2", fg_light = "#425c79" },
|
||||
{ name = "kdenlivetitle", text = "", fg_dark = "#83b8f2", fg_light = "#425c79" },
|
||||
{ name = "kicad_dru", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = "kicad_mod", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = "kicad_pcb", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = "kicad_prl", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = "kicad_pro", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = "kicad_sch", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = "kicad_sym", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = "kicad_wks", text = "", fg_dark = "#ffffff", fg_light = "#333333" },
|
||||
{ name = "ko", text = "", fg_dark = "#dcddd6", fg_light = "#494a47" },
|
||||
{ name = "kpp", text = "", fg_dark = "#f245fb", fg_light = "#a12ea7" },
|
||||
{ name = "kra", text = "", fg_dark = "#f245fb", fg_light = "#a12ea7" },
|
||||
{ name = "krz", text = "", fg_dark = "#f245fb", fg_light = "#a12ea7" },
|
||||
{ name = "ksh", text = "", fg_dark = "#4d5a5e", fg_light = "#3a4446" },
|
||||
{ name = "kt", text = "", fg_dark = "#7f52ff", fg_light = "#5f3ebf" },
|
||||
{ name = "kts", text = "", fg_dark = "#7f52ff", fg_light = "#5f3ebf" },
|
||||
{ name = "lck", text = "", fg_dark = "#bbbbbb", fg_light = "#5e5e5e" },
|
||||
{ name = "leex", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "less", text = "", fg_dark = "#563d7c", fg_light = "#563d7c" },
|
||||
{ name = "lff", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" },
|
||||
{ name = "lhs", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "lib", text = "", fg_dark = "#4d2c0b", fg_light = "#4d2c0b" },
|
||||
{ name = "license", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "liquid", text = "", fg_dark = "#95bf47", fg_light = "#4a6024" },
|
||||
{ name = "lock", text = "", fg_dark = "#bbbbbb", fg_light = "#5e5e5e" },
|
||||
{ name = "log", text = "", fg_dark = "#dddddd", fg_light = "#4a4a4a" },
|
||||
{ name = "lrc", text = "", fg_dark = "#ffb713", fg_light = "#805c0a" },
|
||||
{ name = "lua", text = "", fg_dark = "#51a0cf", fg_light = "#366b8a" },
|
||||
{ name = "luac", text = "", fg_dark = "#51a0cf", fg_light = "#366b8a" },
|
||||
{ name = "luau", text = "", fg_dark = "#00a2ff", fg_light = "#007abf" },
|
||||
{ name = "m", text = "", fg_dark = "#599eff", fg_light = "#3b69aa" },
|
||||
{ name = "m3u", text = "", fg_dark = "#ed95ae", fg_light = "#764a57" },
|
||||
{ name = "m3u8", text = "", fg_dark = "#ed95ae", fg_light = "#764a57" },
|
||||
{ name = "m4a", text = "", fg_dark = "#00afff", fg_light = "#0075aa" },
|
||||
{ name = "m4v", text = "", fg_dark = "#fd971f", fg_light = "#7e4c10" },
|
||||
{ name = "magnet", text = "", fg_dark = "#a51b16", fg_light = "#a51b16" },
|
||||
{ name = "makefile", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "markdown", text = "", fg_dark = "#dddddd", fg_light = "#4a4a4a" },
|
||||
{ name = "material", text = "", fg_dark = "#b83998", fg_light = "#8a2b72" },
|
||||
{ name = "md", text = "", fg_dark = "#dddddd", fg_light = "#4a4a4a" },
|
||||
{ name = "md5", text = "", fg_dark = "#8c86af", fg_light = "#5d5975" },
|
||||
{ name = "mdx", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "mint", text = "", fg_dark = "#87c095", fg_light = "#44604a" },
|
||||
{ name = "mjs", text = "", fg_dark = "#f1e05a", fg_light = "#504b1e" },
|
||||
{ name = "mk", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "mkv", text = "", fg_dark = "#fd971f", fg_light = "#7e4c10" },
|
||||
{ name = "ml", text = "", fg_dark = "#e37933", fg_light = "#975122" },
|
||||
{ name = "mli", text = "", fg_dark = "#e37933", fg_light = "#975122" },
|
||||
{ name = "mm", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "mo", text = "∞", fg_dark = "#9772fb", fg_light = "#654ca7" },
|
||||
{ name = "mobi", text = "", fg_dark = "#eab16d", fg_light = "#755836" },
|
||||
{ name = "mov", text = "", fg_dark = "#fd971f", fg_light = "#7e4c10" },
|
||||
{ name = "mp3", text = "", fg_dark = "#00afff", fg_light = "#0075aa" },
|
||||
{ name = "mp4", text = "", fg_dark = "#fd971f", fg_light = "#7e4c10" },
|
||||
{ name = "mpp", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "msf", text = "", fg_dark = "#137be1", fg_light = "#0e5ca9" },
|
||||
{ name = "mts", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "mustache", text = "", fg_dark = "#e37933", fg_light = "#975122" },
|
||||
{ name = "nfo", text = "", fg_dark = "#ffffcd", fg_light = "#333329" },
|
||||
{ name = "nim", text = "", fg_dark = "#f3d400", fg_light = "#514700" },
|
||||
{ name = "nix", text = "", fg_dark = "#7ebae4", fg_light = "#3f5d72" },
|
||||
{ name = "nswag", text = "", fg_dark = "#85ea2d", fg_light = "#427516" },
|
||||
{ name = "nu", text = ">", fg_dark = "#3aa675", fg_light = "#276f4e" },
|
||||
{ name = "o", text = "", fg_dark = "#9f0500", fg_light = "#9f0500" },
|
||||
{ name = "obj", text = "", fg_dark = "#888888", fg_light = "#5b5b5b" },
|
||||
{ name = "ogg", text = "", fg_dark = "#0075aa", fg_light = "#005880" },
|
||||
{ name = "opus", text = "", fg_dark = "#0075aa", fg_light = "#005880" },
|
||||
{ name = "org", text = "", fg_dark = "#77aa99", fg_light = "#4f7166" },
|
||||
{ name = "otf", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" },
|
||||
{ name = "out", text = "", fg_dark = "#9f0500", fg_light = "#9f0500" },
|
||||
{ name = "part", text = "", fg_dark = "#44cda8", fg_light = "#226654" },
|
||||
{ name = "patch", text = "", fg_dark = "#41535b", fg_light = "#41535b" },
|
||||
{ name = "pck", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "pcm", text = "", fg_dark = "#0075aa", fg_light = "#005880" },
|
||||
{ name = "pdf", text = "", fg_dark = "#b30b00", fg_light = "#b30b00" },
|
||||
{ name = "php", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "pl", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "pls", text = "", fg_dark = "#ed95ae", fg_light = "#764a57" },
|
||||
{ name = "ply", text = "", fg_dark = "#888888", fg_light = "#5b5b5b" },
|
||||
{ name = "pm", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "png", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "po", text = "", fg_dark = "#2596be", fg_light = "#1c708e" },
|
||||
{ name = "pot", text = "", fg_dark = "#2596be", fg_light = "#1c708e" },
|
||||
{ name = "pp", text = "", fg_dark = "#ffa61a", fg_light = "#80530d" },
|
||||
{ name = "ppt", text = "", fg_dark = "#cb4a32", fg_light = "#983826" },
|
||||
{ name = "prisma", text = "", fg_dark = "#5a67d8", fg_light = "#444da2" },
|
||||
{ name = "pro", text = "", fg_dark = "#e4b854", fg_light = "#725c2a" },
|
||||
{ name = "ps1", text = "", fg_dark = "#4273ca", fg_light = "#325698" },
|
||||
{ name = "psb", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "psd", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "psd1", text = "", fg_dark = "#6975c4", fg_light = "#4f5893" },
|
||||
{ name = "psm1", text = "", fg_dark = "#6975c4", fg_light = "#4f5893" },
|
||||
{ name = "pub", text = "", fg_dark = "#e3c58e", fg_light = "#4c422f" },
|
||||
{ name = "pxd", text = "", fg_dark = "#5aa7e4", fg_light = "#3c6f98" },
|
||||
{ name = "pxi", text = "", fg_dark = "#5aa7e4", fg_light = "#3c6f98" },
|
||||
{ name = "py", text = "", fg_dark = "#ffbc03", fg_light = "#805e02" },
|
||||
{ name = "pyc", text = "", fg_dark = "#ffe291", fg_light = "#332d1d" },
|
||||
{ name = "pyd", text = "", fg_dark = "#ffe291", fg_light = "#332d1d" },
|
||||
{ name = "pyi", text = "", fg_dark = "#ffbc03", fg_light = "#805e02" },
|
||||
{ name = "pyo", text = "", fg_dark = "#ffe291", fg_light = "#332d1d" },
|
||||
{ name = "pyx", text = "", fg_dark = "#5aa7e4", fg_light = "#3c6f98" },
|
||||
{ name = "qm", text = "", fg_dark = "#2596be", fg_light = "#1c708e" },
|
||||
{ name = "qml", text = "", fg_dark = "#40cd52", fg_light = "#2b8937" },
|
||||
{ name = "qrc", text = "", fg_dark = "#40cd52", fg_light = "#2b8937" },
|
||||
{ name = "qss", text = "", fg_dark = "#40cd52", fg_light = "#2b8937" },
|
||||
{ name = "query", text = "", fg_dark = "#90a850", fg_light = "#607035" },
|
||||
{ name = "r", text = "", fg_dark = "#2266ba", fg_light = "#1a4c8c" },
|
||||
{ name = "rake", text = "", fg_dark = "#701516", fg_light = "#701516" },
|
||||
{ name = "rar", text = "", fg_dark = "#eca517", fg_light = "#76520c" },
|
||||
{ name = "razor", text = "", fg_dark = "#512bd4", fg_light = "#512bd4" },
|
||||
{ name = "rb", text = "", fg_dark = "#701516", fg_light = "#701516" },
|
||||
{ name = "res", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" },
|
||||
{ name = "resi", text = "", fg_dark = "#f55385", fg_light = "#a33759" },
|
||||
{ name = "rlib", text = "", fg_dark = "#dea584", fg_light = "#6f5242" },
|
||||
{ name = "rmd", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "rproj", text = "", fg_dark = "#358a5b", fg_light = "#286844" },
|
||||
{ name = "rs", text = "", fg_dark = "#dea584", fg_light = "#6f5242" },
|
||||
{ name = "rss", text = "", fg_dark = "#fb9d3b", fg_light = "#7e4e1e" },
|
||||
{ name = "sass", text = "", fg_dark = "#f55385", fg_light = "#a33759" },
|
||||
{ name = "sbt", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" },
|
||||
{ name = "sc", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" },
|
||||
{ name = "scad", text = "", fg_dark = "#f9d72c", fg_light = "#53480f" },
|
||||
{ name = "scala", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" },
|
||||
{ name = "scm", text = "", fg_dark = "#eeeeee", fg_light = "#303030" },
|
||||
{ name = "scss", text = "", fg_dark = "#f55385", fg_light = "#a33759" },
|
||||
{ name = "sh", text = "", fg_dark = "#4d5a5e", fg_light = "#3a4446" },
|
||||
{ name = "sha1", text = "", fg_dark = "#8c86af", fg_light = "#5d5975" },
|
||||
{ name = "sha224", text = "", fg_dark = "#8c86af", fg_light = "#5d5975" },
|
||||
{ name = "sha256", text = "", fg_dark = "#8c86af", fg_light = "#5d5975" },
|
||||
{ name = "sha384", text = "", fg_dark = "#8c86af", fg_light = "#5d5975" },
|
||||
{ name = "sha512", text = "", fg_dark = "#8c86af", fg_light = "#5d5975" },
|
||||
{ name = "sig", text = "λ", fg_dark = "#e37933", fg_light = "#975122" },
|
||||
{ name = "signature", text = "λ", fg_dark = "#e37933", fg_light = "#975122" },
|
||||
{ name = "skp", text = "", fg_dark = "#839463", fg_light = "#576342" },
|
||||
{ name = "sldasm", text = "", fg_dark = "#839463", fg_light = "#576342" },
|
||||
{ name = "sldprt", text = "", fg_dark = "#839463", fg_light = "#576342" },
|
||||
{ name = "slim", text = "", fg_dark = "#e34c26", fg_light = "#aa391c" },
|
||||
{ name = "sln", text = "", fg_dark = "#854cc7", fg_light = "#643995" },
|
||||
{ name = "slvs", text = "", fg_dark = "#839463", fg_light = "#576342" },
|
||||
{ name = "sml", text = "λ", fg_dark = "#e37933", fg_light = "#975122" },
|
||||
{ name = "so", text = "", fg_dark = "#dcddd6", fg_light = "#494a47" },
|
||||
{ name = "sol", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "spec.js", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "spec.jsx", text = "", fg_dark = "#20c2e3", fg_light = "#158197" },
|
||||
{ name = "spec.ts", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "spec.tsx", text = "", fg_dark = "#1354bf", fg_light = "#1354bf" },
|
||||
{ name = "sql", text = "", fg_dark = "#dad8d8", fg_light = "#494848" },
|
||||
{ name = "sqlite", text = "", fg_dark = "#dad8d8", fg_light = "#494848" },
|
||||
{ name = "sqlite3", text = "", fg_dark = "#dad8d8", fg_light = "#494848" },
|
||||
{ name = "srt", text = "", fg_dark = "#ffb713", fg_light = "#805c0a" },
|
||||
{ name = "ssa", text = "", fg_dark = "#ffb713", fg_light = "#805c0a" },
|
||||
{ name = "ste", text = "", fg_dark = "#839463", fg_light = "#576342" },
|
||||
{ name = "step", text = "", fg_dark = "#839463", fg_light = "#576342" },
|
||||
{ name = "stl", text = "", fg_dark = "#888888", fg_light = "#5b5b5b" },
|
||||
{ name = "stp", text = "", fg_dark = "#839463", fg_light = "#576342" },
|
||||
{ name = "strings", text = "", fg_dark = "#2596be", fg_light = "#1c708e" },
|
||||
{ name = "styl", text = "", fg_dark = "#8dc149", fg_light = "#466024" },
|
||||
{ name = "sub", text = "", fg_dark = "#ffb713", fg_light = "#805c0a" },
|
||||
{ name = "sublime", text = "", fg_dark = "#e37933", fg_light = "#975122" },
|
||||
{ name = "suo", text = "", fg_dark = "#854cc7", fg_light = "#643995" },
|
||||
{ name = "sv", text = "", fg_dark = "#019833", fg_light = "#017226" },
|
||||
{ name = "svelte", text = "", fg_dark = "#ff3e00", fg_light = "#bf2e00" },
|
||||
{ name = "svg", text = "", fg_dark = "#ffb13b", fg_light = "#80581e" },
|
||||
{ name = "svh", text = "", fg_dark = "#019833", fg_light = "#017226" },
|
||||
{ name = "swift", text = "", fg_dark = "#e37933", fg_light = "#975122" },
|
||||
{ name = "t", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "tbc", text = "", fg_dark = "#1e5cb3", fg_light = "#1e5cb3" },
|
||||
{ name = "tcl", text = "", fg_dark = "#1e5cb3", fg_light = "#1e5cb3" },
|
||||
{ name = "templ", text = "", fg_dark = "#dbbd30", fg_light = "#6e5e18" },
|
||||
{ name = "terminal", text = "", fg_dark = "#31b53e", fg_light = "#217929" },
|
||||
{ name = "test.js", text = "", fg_dark = "#cbcb41", fg_light = "#666620" },
|
||||
{ name = "test.jsx", text = "", fg_dark = "#20c2e3", fg_light = "#158197" },
|
||||
{ name = "test.ts", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "test.tsx", text = "", fg_dark = "#1354bf", fg_light = "#1354bf" },
|
||||
{ name = "tex", text = "", fg_dark = "#3d6117", fg_light = "#3d6117" },
|
||||
{ name = "tf", text = "", fg_dark = "#5f43e9", fg_light = "#4732af" },
|
||||
{ name = "tfvars", text = "", fg_dark = "#5f43e9", fg_light = "#4732af" },
|
||||
{ name = "tgz", text = "", fg_dark = "#eca517", fg_light = "#76520c" },
|
||||
{ name = "tmux", text = "", fg_dark = "#14ba19", fg_light = "#0f8c13" },
|
||||
{ name = "toml", text = "", fg_dark = "#9c4221", fg_light = "#753219" },
|
||||
{ name = "torrent", text = "", fg_dark = "#44cda8", fg_light = "#226654" },
|
||||
{ name = "tres", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "ts", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "tscn", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "tsconfig", text = "", fg_dark = "#ff8700", fg_light = "#aa5a00" },
|
||||
{ name = "tsx", text = "", fg_dark = "#1354bf", fg_light = "#1354bf" },
|
||||
{ name = "ttf", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" },
|
||||
{ name = "twig", text = "", fg_dark = "#8dc149", fg_light = "#466024" },
|
||||
{ name = "txt", text = "", fg_dark = "#89e051", fg_light = "#447028" },
|
||||
{ name = "txz", text = "", fg_dark = "#eca517", fg_light = "#76520c" },
|
||||
{ name = "typoscript", text = "", fg_dark = "#ff8700", fg_light = "#aa5a00" },
|
||||
{ name = "ui", text = "", fg_dark = "#0c306e", fg_light = "#0c306e" },
|
||||
{ name = "v", text = "", fg_dark = "#019833", fg_light = "#017226" },
|
||||
{ name = "vala", text = "", fg_dark = "#7239b3", fg_light = "#562b86" },
|
||||
{ name = "vh", text = "", fg_dark = "#019833", fg_light = "#017226" },
|
||||
{ name = "vhd", text = "", fg_dark = "#019833", fg_light = "#017226" },
|
||||
{ name = "vhdl", text = "", fg_dark = "#019833", fg_light = "#017226" },
|
||||
{ name = "vim", text = "", fg_dark = "#019833", fg_light = "#017226" },
|
||||
{ name = "vsh", text = "", fg_dark = "#5d87bf", fg_light = "#3e5a7f" },
|
||||
{ name = "vsix", text = "", fg_dark = "#854cc7", fg_light = "#643995" },
|
||||
{ name = "vue", text = "", fg_dark = "#8dc149", fg_light = "#466024" },
|
||||
{ name = "wasm", text = "", fg_dark = "#5c4cdb", fg_light = "#4539a4" },
|
||||
{ name = "wav", text = "", fg_dark = "#00afff", fg_light = "#0075aa" },
|
||||
{ name = "webm", text = "", fg_dark = "#fd971f", fg_light = "#7e4c10" },
|
||||
{ name = "webmanifest", text = "", fg_dark = "#f1e05a", fg_light = "#504b1e" },
|
||||
{ name = "webp", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" },
|
||||
{ name = "webpack", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "wma", text = "", fg_dark = "#00afff", fg_light = "#0075aa" },
|
||||
{ name = "woff", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" },
|
||||
{ name = "woff2", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" },
|
||||
{ name = "wrl", text = "", fg_dark = "#888888", fg_light = "#5b5b5b" },
|
||||
{ name = "wrz", text = "", fg_dark = "#888888", fg_light = "#5b5b5b" },
|
||||
{ name = "x", text = "", fg_dark = "#599eff", fg_light = "#3b69aa" },
|
||||
{ name = "xaml", text = "", fg_dark = "#512bd4", fg_light = "#512bd4" },
|
||||
{ name = "xcf", text = "", fg_dark = "#635b46", fg_light = "#4a4434" },
|
||||
{ name = "xcplayground", text = "", fg_dark = "#e37933", fg_light = "#975122" },
|
||||
{ name = "xcstrings", text = "", fg_dark = "#2596be", fg_light = "#1c708e" },
|
||||
{ name = "xls", text = "", fg_dark = "#207245", fg_light = "#207245" },
|
||||
{ name = "xlsx", text = "", fg_dark = "#207245", fg_light = "#207245" },
|
||||
{ name = "xm", text = "", fg_dark = "#519aba", fg_light = "#36677c" },
|
||||
{ name = "xml", text = "", fg_dark = "#e37933", fg_light = "#975122" },
|
||||
{ name = "xpi", text = "", fg_dark = "#ff1b01", fg_light = "#bf1401" },
|
||||
{ name = "xul", text = "", fg_dark = "#e37933", fg_light = "#975122" },
|
||||
{ name = "xz", text = "", fg_dark = "#eca517", fg_light = "#76520c" },
|
||||
{ name = "yaml", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "yml", text = "", fg_dark = "#6d8086", fg_light = "#526064" },
|
||||
{ name = "zig", text = "", fg_dark = "#f69a1b", fg_light = "#7b4d0e" },
|
||||
{ name = "zip", text = "", fg_dark = "#eca517", fg_light = "#76520c" },
|
||||
{ name = "zsh", text = "", fg_dark = "#89e051", fg_light = "#447028" },
|
||||
{ name = "zst", text = "", fg_dark = "#eca517", fg_light = "#76520c" },
|
||||
]
|
||||
conds = [
|
||||
# Special files
|
||||
{ if = "orphan", text = "" },
|
||||
{ if = "link" , text = "" },
|
||||
{ if = "block" , text = "" },
|
||||
{ if = "char" , text = "" },
|
||||
{ if = "fifo" , text = "" },
|
||||
{ if = "sock" , text = "" },
|
||||
{ if = "sticky", text = "" },
|
||||
|
||||
# Text
|
||||
{ name = "*.conf", text = "", fg = "#6d8086" },
|
||||
{ name = "*.ini" , text = "", fg = "#6d8086" },
|
||||
{ name = "*.json", text = "", fg = "#cbcb41" },
|
||||
{ name = "*.kdl" , text = "", fg = "#6d8086" },
|
||||
{ name = "*.md" , text = "", fg = "#ffffff" },
|
||||
{ name = "*.toml", text = "", fg = "#ffffff" },
|
||||
{ name = "*.txt" , text = "", fg = "#89e051" },
|
||||
{ name = "*.yaml", text = "", fg = "#6d8086" },
|
||||
{ name = "*.yml" , text = "", fg = "#6d8086" },
|
||||
|
||||
# Archives
|
||||
{ name = "*.7z" , text = "" },
|
||||
{ name = "*.bz2", text = "" },
|
||||
{ name = "*.gz" , text = "" },
|
||||
{ name = "*.rar", text = "" },
|
||||
{ name = "*.tar", text = "" },
|
||||
{ name = "*.xz" , text = "" },
|
||||
{ name = "*.zip", text = "" },
|
||||
|
||||
# Images
|
||||
{ name = "*.HEIC", text = "", fg = "#a074c4" },
|
||||
{ name = "*.avif", text = "", fg = "#a074c4" },
|
||||
{ name = "*.bmp" , text = "", fg = "#a074c4" },
|
||||
{ name = "*.gif" , text = "", fg = "#a074c4" },
|
||||
{ name = "*.ico" , text = "", fg = "#cbcb41" },
|
||||
{ name = "*.jpeg", text = "", fg = "#a074c4" },
|
||||
{ name = "*.jpg" , text = "", fg = "#a074c4" },
|
||||
{ name = "*.png" , text = "", fg = "#a074c4" },
|
||||
{ name = "*.svg" , text = "", fg = "#FFB13B" },
|
||||
{ name = "*.webp", text = "", fg = "#a074c4" },
|
||||
|
||||
# Movies
|
||||
{ name = "*.avi" , text = "", fg = "#FD971F" },
|
||||
{ name = "*.mkv" , text = "", fg = "#FD971F" },
|
||||
{ name = "*.mov" , text = "", fg = "#FD971F" },
|
||||
{ name = "*.mp4" , text = "", fg = "#FD971F" },
|
||||
{ name = "*.webm", text = "", fg = "#FD971F" },
|
||||
|
||||
# Audio
|
||||
{ name = "*.aac" , text = "", fg = "#66D8EF" },
|
||||
{ name = "*.flac", text = "", fg = "#66D8EF" },
|
||||
{ name = "*.m4a" , text = "", fg = "#66D8EF" },
|
||||
{ name = "*.mp3" , text = "", fg = "#66D8EF" },
|
||||
{ name = "*.ogg" , text = "", fg = "#66D8EF" },
|
||||
{ name = "*.wav" , text = "", fg = "#66D8EF" },
|
||||
|
||||
# Documents
|
||||
{ name = "*.csv" , text = "", fg = "#89e051" },
|
||||
{ name = "*.doc" , text = "", fg = "#185abd" },
|
||||
{ name = "*.doct", text = "", fg = "#185abd" },
|
||||
{ name = "*.docx", text = "", fg = "#185abd" },
|
||||
{ name = "*.dot" , text = "", fg = "#185abd" },
|
||||
{ name = "*.ods" , text = "", fg = "#207245" },
|
||||
{ name = "*.ots" , text = "", fg = "#207245" },
|
||||
{ name = "*.pdf" , text = "", fg = "#b30b00" },
|
||||
{ name = "*.pom" , text = "", fg = "#cc3e44" },
|
||||
{ name = "*.pot" , text = "", fg = "#cb4a32" },
|
||||
{ name = "*.potx", text = "", fg = "#cb4a32" },
|
||||
{ name = "*.ppm" , text = "", fg = "#a074c4" },
|
||||
{ name = "*.ppmx", text = "", fg = "#cb4a32" },
|
||||
{ name = "*.pps" , text = "", fg = "#cb4a32" },
|
||||
{ name = "*.ppsx", text = "", fg = "#cb4a32" },
|
||||
{ name = "*.ppt" , text = "", fg = "#cb4a32" },
|
||||
{ name = "*.pptx", text = "", fg = "#cb4a32" },
|
||||
{ name = "*.xlc" , text = "", fg = "#207245" },
|
||||
{ name = "*.xlm" , text = "", fg = "#207245" },
|
||||
{ name = "*.xls" , text = "", fg = "#207245" },
|
||||
{ name = "*.xlsm", text = "", fg = "#207245" },
|
||||
{ name = "*.xlsx", text = "", fg = "#207245" },
|
||||
{ name = "*.xlt" , text = "", fg = "#207245" },
|
||||
|
||||
# Lockfiles
|
||||
{ name = "*.lock", text = "", fg = "#bbbbbb" },
|
||||
|
||||
# Misc
|
||||
{ name = "*.bin", text = "", fg = "#9F0500" },
|
||||
{ name = "*.exe", text = "", fg = "#9F0500" },
|
||||
{ name = "*.pkg", text = "", fg = "#9F0500" },
|
||||
|
||||
# Dotfiles
|
||||
{ name = ".DS_Store" , text = "", fg = "#41535b" },
|
||||
{ name = ".bashprofile" , text = "", fg = "#89e051" },
|
||||
{ name = ".bashrc" , text = "", fg = "#89e051" },
|
||||
{ name = ".gitattributes", text = "", fg = "#41535b" },
|
||||
{ name = ".gitignore" , text = "", fg = "#41535b" },
|
||||
{ name = ".gitmodules" , text = "", fg = "#41535b" },
|
||||
{ name = ".vimrc" , text = "", fg = "#019833" },
|
||||
{ name = ".zprofile" , text = "", fg = "#89e051" },
|
||||
{ name = ".zshenv" , text = "", fg = "#89e051" },
|
||||
{ name = ".zshrc" , text = "", fg = "#89e051" },
|
||||
|
||||
# Named files
|
||||
{ name = "COPYING" , text = "", fg = "#cbcb41" },
|
||||
{ name = "Containerfile", text = "", fg = "#458ee6" },
|
||||
{ name = "Dockerfile" , text = "", fg = "#458ee6" },
|
||||
{ name = "LICENSE" , text = "", fg = "#d0bf41" },
|
||||
|
||||
# Directories
|
||||
{ name = ".config/" , text = "" },
|
||||
{ name = ".git/" , text = "" },
|
||||
{ name = "Desktop/" , text = "" },
|
||||
{ name = "Development/", text = "" },
|
||||
{ name = "Documents/" , text = "" },
|
||||
{ name = "Downloads/" , text = "" },
|
||||
{ name = "Library/" , text = "" },
|
||||
{ name = "Movies/" , text = "" },
|
||||
{ name = "Music/" , text = "" },
|
||||
{ name = "Pictures/" , text = "" },
|
||||
{ name = "Public/" , text = "" },
|
||||
{ name = "Videos/" , text = "" },
|
||||
|
||||
# Default
|
||||
{ name = "*" , text = "" },
|
||||
{ name = "*/", text = "" },
|
||||
# Fallback
|
||||
{ if = "dir", text = "" },
|
||||
{ if = "exec", text = "" },
|
||||
{ if = "!dir", text = "" },
|
||||
]
|
||||
|
||||
# : }}}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ sort_by = "alphabetical"
|
|||
sort_sensitive = false
|
||||
sort_reverse = false
|
||||
sort_dir_first = true
|
||||
sort_translit = false
|
||||
linemode = "none"
|
||||
show_hidden = false
|
||||
show_symlink = true
|
||||
scrolloff = 5
|
||||
mouse_events = [ "click", "scroll" ]
|
||||
|
||||
[preview]
|
||||
tab_size = 2
|
||||
|
|
@ -27,16 +29,16 @@ ueberzug_offset = [ 0, 0, 0, 0 ]
|
|||
[opener]
|
||||
edit = [
|
||||
{ run = '${EDITOR:=vi} "$@"', desc = "$EDITOR", block = true, for = "unix" },
|
||||
{ run = 'code "%*"', orphan = true, desc = "code", for = "windows" },
|
||||
{ run = 'code -w "%*"', block = true, desc = "code (block)", for = "windows" },
|
||||
{ run = 'code %*', orphan = true, desc = "code", for = "windows" },
|
||||
{ run = 'code -w %*', block = true, desc = "code (block)", for = "windows" },
|
||||
]
|
||||
open = [
|
||||
{ run = 'xdg-open "$@"', desc = "Open", for = "linux" },
|
||||
{ run = 'xdg-open "$1"', desc = "Open", for = "linux" },
|
||||
{ run = 'open "$@"', desc = "Open", for = "macos" },
|
||||
{ run = 'start "" "%1"', orphan = true, desc = "Open", for = "windows" },
|
||||
]
|
||||
reveal = [
|
||||
{ run = 'xdg-open "$(dirname "$0")"', desc = "Reveal", for = "linux" },
|
||||
{ run = 'xdg-open "$(dirname "$1")"', desc = "Reveal", for = "linux" },
|
||||
{ run = 'open -R "$1"', desc = "Reveal", for = "macos" },
|
||||
{ run = 'explorer /select, "%1"', orphan = true, desc = "Reveal", for = "windows" },
|
||||
{ run = '''exiftool "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show EXIF", for = "unix" },
|
||||
|
|
@ -46,8 +48,8 @@ extract = [
|
|||
{ run = 'unar "%1"', desc = "Extract here", for = "windows" },
|
||||
]
|
||||
play = [
|
||||
{ run = 'mpv "$@"', orphan = true, for = "unix" },
|
||||
{ run = 'mpv "%1"', orphan = true, for = "windows" },
|
||||
{ run = 'mpv --force-window "$@"', orphan = true, for = "unix" },
|
||||
{ run = 'mpv --force-window "%1"', orphan = true, for = "windows" },
|
||||
{ run = '''mediainfo "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show media info", for = "unix" },
|
||||
]
|
||||
|
||||
|
|
@ -60,13 +62,13 @@ rules = [
|
|||
{ mime = "{audio,video}/*", use = [ "play", "reveal" ] },
|
||||
{ mime = "inode/x-empty", use = [ "edit", "reveal" ] },
|
||||
|
||||
{ mime = "application/*zip", use = [ "extract", "reveal" ] },
|
||||
{ mime = "application/{,g}zip", use = [ "extract", "reveal" ] },
|
||||
{ mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", use = [ "extract", "reveal" ] },
|
||||
|
||||
{ mime = "application/json", use = [ "edit", "reveal" ] },
|
||||
{ mime = "application/{json,x-ndjson}", use = [ "edit", "reveal" ] },
|
||||
{ mime = "*/javascript", use = [ "edit", "reveal" ] },
|
||||
|
||||
{ mime = "*", use = [ "open", "reveal" ] },
|
||||
{ name = "*", use = [ "open", "reveal" ] },
|
||||
]
|
||||
|
||||
[tasks]
|
||||
|
|
@ -79,14 +81,21 @@ suppress_preload = false
|
|||
|
||||
[plugin]
|
||||
|
||||
fetchers = [
|
||||
# Mimetype
|
||||
{ id = "mime", name = "*", run = "mime", if = "!mime", prio = "high" },
|
||||
]
|
||||
preloaders = [
|
||||
{ name = "*", cond = "!mime", run = "mime", multi = true, prio = "high" },
|
||||
# Image
|
||||
{ mime = "image/{avif,heic,jxl,svg+xml}", run = "magick" },
|
||||
{ mime = "image/*", run = "image" },
|
||||
# Video
|
||||
{ mime = "video/*", run = "video" },
|
||||
# PDF
|
||||
{ mime = "application/pdf", run = "pdf" },
|
||||
# Font
|
||||
{ mime = "font/*", run = "font" },
|
||||
{ mime = "application/vnd.ms-opentype", run = "font" },
|
||||
]
|
||||
previewers = [
|
||||
{ name = "*/", run = "folder", sync = true },
|
||||
|
|
@ -94,22 +103,27 @@ previewers = [
|
|||
{ mime = "text/*", run = "code" },
|
||||
{ mime = "*/{xml,javascript,x-wine-extension-ini}", run = "code" },
|
||||
# JSON
|
||||
{ mime = "application/json", run = "json" },
|
||||
{ mime = "application/{json,x-ndjson}", run = "json" },
|
||||
# Image
|
||||
{ mime = "image/vnd.djvu", run = "noop" },
|
||||
{ mime = "image/{avif,heic,jxl,svg+xml}", run = "magick" },
|
||||
{ mime = "image/*", run = "image" },
|
||||
# Video
|
||||
{ mime = "video/*", run = "video" },
|
||||
# PDF
|
||||
{ mime = "application/pdf", run = "pdf" },
|
||||
# Archive
|
||||
{ mime = "application/*zip", run = "archive" },
|
||||
{ mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", run = "archive" },
|
||||
{ mime = "application/{,g}zip", run = "archive" },
|
||||
{ mime = "application/x-{tar,bzip*,7z-compressed,xz,rar,iso9660-image}", run = "archive" },
|
||||
# Font
|
||||
{ mime = "font/*", run = "font" },
|
||||
{ mime = "application/vnd.ms-opentype", run = "font" },
|
||||
# Fallback
|
||||
{ name = "*", run = "file" },
|
||||
]
|
||||
|
||||
[input]
|
||||
cursor_blink = true
|
||||
|
||||
# cd
|
||||
cd_title = "Change directory:"
|
||||
cd_origin = "top-center"
|
||||
|
|
@ -174,8 +188,7 @@ open_offset = [ 0, 1, 50, 7 ]
|
|||
sort_by = "none"
|
||||
sort_sensitive = false
|
||||
sort_reverse = false
|
||||
sort_translit = false
|
||||
|
||||
[log]
|
||||
enabled = false
|
||||
|
||||
[headsup]
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
use crate::MERGED_YAZI;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Headsup {
|
||||
// TODO: remove this once Yazi 0.3 is released --
|
||||
pub disable_exec_warn: bool,
|
||||
}
|
||||
|
||||
impl Default for Headsup {
|
||||
fn default() -> Self { toml::from_str(&MERGED_YAZI).unwrap() }
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Headsup {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
headsup: Shadow,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
struct Shadow {
|
||||
#[serde(default)]
|
||||
disable_exec_warn: bool,
|
||||
}
|
||||
|
||||
let outer = Outer::deserialize(deserializer)?;
|
||||
|
||||
Ok(Self { disable_exec_warn: outer.headsup.disable_exec_warn })
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
mod headsup;
|
||||
|
||||
pub use headsup::*;
|
||||
|
|
@ -1,14 +1,15 @@
|
|||
use std::{borrow::Cow, collections::VecDeque, sync::atomic::Ordering};
|
||||
use std::{borrow::Cow, collections::VecDeque};
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use serde::Deserialize;
|
||||
use yazi_shared::event::Cmd;
|
||||
|
||||
use super::Key;
|
||||
use crate::DEPRECATED_EXEC;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub struct Control {
|
||||
#[serde(deserialize_with = "super::deserialize_on")]
|
||||
pub on: Vec<Key>,
|
||||
#[serde(deserialize_with = "super::deserialize_run")]
|
||||
pub run: Vec<Cmd>,
|
||||
pub desc: Option<String>,
|
||||
}
|
||||
|
|
@ -24,7 +25,7 @@ impl Control {
|
|||
|
||||
#[inline]
|
||||
pub fn run(&self) -> String {
|
||||
self.run.iter().map(|e| e.to_string()).collect::<Vec<_>>().join("; ")
|
||||
self.run.iter().map(|c| c.to_string()).collect::<Vec<_>>().join("; ")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -40,33 +41,3 @@ impl Control {
|
|||
|| self.on().to_lowercase().contains(&s)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove this once Yazi 0.3 is released
|
||||
impl<'de> Deserialize<'de> for Control {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
pub struct Shadow {
|
||||
pub on: Vec<Key>,
|
||||
pub run: Option<VecCmd>,
|
||||
pub exec: Option<VecCmd>,
|
||||
pub desc: Option<String>,
|
||||
}
|
||||
|
||||
let shadow = Shadow::deserialize(deserializer)?;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct VecCmd(#[serde(deserialize_with = "super::run_deserialize")] Vec<Cmd>);
|
||||
|
||||
if shadow.exec.is_some() {
|
||||
DEPRECATED_EXEC.store(true, Ordering::Relaxed);
|
||||
}
|
||||
let Some(run) = shadow.run.or(shadow.exec) else {
|
||||
return Err(serde::de::Error::custom("missing field `run` within `[keymap]`"));
|
||||
};
|
||||
|
||||
Ok(Self { on: shadow.on, run: run.0, desc: shadow.desc })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
83
yazi-config/src/keymap/deserializers.rs
Normal file
83
yazi-config/src/keymap/deserializers.rs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
use std::{fmt, str::FromStr};
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::{de::{self, Visitor}, Deserializer};
|
||||
use yazi_shared::event::Cmd;
|
||||
|
||||
use crate::keymap::Key;
|
||||
|
||||
pub(super) fn deserialize_on<'de, D>(deserializer: D) -> Result<Vec<Key>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct OnVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for OnVisitor {
|
||||
type Value = Vec<Key>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a `on` string or array of strings within keymap.toml")
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: de::SeqAccess<'de>,
|
||||
{
|
||||
let mut cmds = vec![];
|
||||
while let Some(value) = &seq.next_element::<String>()? {
|
||||
cmds.push(Key::from_str(value).map_err(de::Error::custom)?);
|
||||
}
|
||||
if cmds.is_empty() {
|
||||
return Err(de::Error::custom("`on` within keymap.toml cannot be empty"));
|
||||
}
|
||||
Ok(cmds)
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(vec![Key::from_str(value).map_err(de::Error::custom)?])
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(OnVisitor)
|
||||
}
|
||||
|
||||
pub(super) fn deserialize_run<'de, D>(deserializer: D) -> Result<Vec<Cmd>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct RunVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for RunVisitor {
|
||||
type Value = Vec<Cmd>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a `run` string or array of strings within keymap.toml")
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: de::SeqAccess<'de>,
|
||||
{
|
||||
let mut cmds = vec![];
|
||||
while let Some(value) = &seq.next_element::<String>()? {
|
||||
cmds.push(Cmd::from_str(value).map_err(de::Error::custom)?);
|
||||
}
|
||||
if cmds.is_empty() {
|
||||
return Err(de::Error::custom("`run` within keymap.toml cannot be empty"));
|
||||
}
|
||||
Ok(cmds)
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(vec![Cmd::from_str(value).map_err(de::Error::custom)?])
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(RunVisitor)
|
||||
}
|
||||
|
|
@ -2,34 +2,30 @@ use std::{fmt::{Display, Write}, str::FromStr};
|
|||
|
||||
use anyhow::bail;
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Hash)]
|
||||
#[serde(try_from = "String")]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Key {
|
||||
pub code: KeyCode,
|
||||
pub shift: bool,
|
||||
pub ctrl: bool,
|
||||
pub alt: bool,
|
||||
pub super_: bool,
|
||||
}
|
||||
|
||||
impl Key {
|
||||
#[inline]
|
||||
pub fn plain(&self) -> Option<char> {
|
||||
match self.code {
|
||||
KeyCode::Char(c) if !self.ctrl && !self.alt => Some(c),
|
||||
KeyCode::Char(c) if !self.ctrl && !self.alt && !self.super_ => Some(c),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_enter(&self) -> bool {
|
||||
matches!(self, Key { code: KeyCode::Enter, shift: false, ctrl: false, alt: false })
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Key {
|
||||
fn default() -> Self { Self { code: KeyCode::Null, shift: false, ctrl: false, alt: false } }
|
||||
fn default() -> Self {
|
||||
Self { code: KeyCode::Null, shift: false, ctrl: false, alt: false, super_: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KeyEvent> for Key {
|
||||
|
|
@ -56,6 +52,7 @@ impl From<KeyEvent> for Key {
|
|||
shift,
|
||||
ctrl: value.modifiers.contains(KeyModifiers::CONTROL),
|
||||
alt: value.modifiers.contains(KeyModifiers::ALT),
|
||||
super_: value.modifiers.contains(KeyModifiers::SUPER),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -77,45 +74,46 @@ impl FromStr for Key {
|
|||
}
|
||||
|
||||
let mut it = s[1..s.len() - 1].split_inclusive('-').peekable();
|
||||
while let Some(x) = it.next() {
|
||||
match x {
|
||||
"S-" => key.shift = true,
|
||||
"C-" => key.ctrl = true,
|
||||
"A-" => key.alt = true,
|
||||
while let Some(next) = it.next() {
|
||||
match next.to_ascii_lowercase().as_str() {
|
||||
"s-" => key.shift = true,
|
||||
"c-" => key.ctrl = true,
|
||||
"a-" => key.alt = true,
|
||||
"d-" => key.super_ = true,
|
||||
|
||||
"Space" => key.code = KeyCode::Char(' '),
|
||||
"Backspace" => key.code = KeyCode::Backspace,
|
||||
"Enter" => key.code = KeyCode::Enter,
|
||||
"Left" => key.code = KeyCode::Left,
|
||||
"Right" => key.code = KeyCode::Right,
|
||||
"Up" => key.code = KeyCode::Up,
|
||||
"Down" => key.code = KeyCode::Down,
|
||||
"Home" => key.code = KeyCode::Home,
|
||||
"End" => key.code = KeyCode::End,
|
||||
"PageUp" => key.code = KeyCode::PageUp,
|
||||
"PageDown" => key.code = KeyCode::PageDown,
|
||||
"Tab" => key.code = KeyCode::Tab,
|
||||
"BackTab" => key.code = KeyCode::BackTab,
|
||||
"Delete" => key.code = KeyCode::Delete,
|
||||
"Insert" => key.code = KeyCode::Insert,
|
||||
"F1" => key.code = KeyCode::F(1),
|
||||
"F2" => key.code = KeyCode::F(2),
|
||||
"F3" => key.code = KeyCode::F(3),
|
||||
"F4" => key.code = KeyCode::F(4),
|
||||
"F5" => key.code = KeyCode::F(5),
|
||||
"F6" => key.code = KeyCode::F(6),
|
||||
"F7" => key.code = KeyCode::F(7),
|
||||
"F8" => key.code = KeyCode::F(8),
|
||||
"F9" => key.code = KeyCode::F(9),
|
||||
"F10" => key.code = KeyCode::F(10),
|
||||
"F11" => key.code = KeyCode::F(11),
|
||||
"F12" => key.code = KeyCode::F(12),
|
||||
"Esc" => key.code = KeyCode::Esc,
|
||||
"space" => key.code = KeyCode::Char(' '),
|
||||
"backspace" => key.code = KeyCode::Backspace,
|
||||
"enter" => key.code = KeyCode::Enter,
|
||||
"left" => key.code = KeyCode::Left,
|
||||
"right" => key.code = KeyCode::Right,
|
||||
"up" => key.code = KeyCode::Up,
|
||||
"down" => key.code = KeyCode::Down,
|
||||
"home" => key.code = KeyCode::Home,
|
||||
"end" => key.code = KeyCode::End,
|
||||
"pageup" => key.code = KeyCode::PageUp,
|
||||
"pagedown" => key.code = KeyCode::PageDown,
|
||||
"tab" => key.code = KeyCode::Tab,
|
||||
"backtab" => key.code = KeyCode::BackTab,
|
||||
"delete" => key.code = KeyCode::Delete,
|
||||
"insert" => key.code = KeyCode::Insert,
|
||||
"f1" => key.code = KeyCode::F(1),
|
||||
"f2" => key.code = KeyCode::F(2),
|
||||
"f3" => key.code = KeyCode::F(3),
|
||||
"f4" => key.code = KeyCode::F(4),
|
||||
"f5" => key.code = KeyCode::F(5),
|
||||
"f6" => key.code = KeyCode::F(6),
|
||||
"f7" => key.code = KeyCode::F(7),
|
||||
"f8" => key.code = KeyCode::F(8),
|
||||
"f9" => key.code = KeyCode::F(9),
|
||||
"f10" => key.code = KeyCode::F(10),
|
||||
"f11" => key.code = KeyCode::F(11),
|
||||
"f12" => key.code = KeyCode::F(12),
|
||||
"esc" => key.code = KeyCode::Esc,
|
||||
|
||||
c if it.peek().is_none() => {
|
||||
key.code = KeyCode::Char(c.chars().next().unwrap());
|
||||
}
|
||||
k => bail!("unknown key: {k}"),
|
||||
_ => match next {
|
||||
s if it.peek().is_none() => key.code = KeyCode::Char(s.chars().next().unwrap()),
|
||||
s => bail!("unknown key: {s}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -126,12 +124,6 @@ impl FromStr for Key {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Key {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> { Self::from_str(&s) }
|
||||
}
|
||||
|
||||
impl Display for Key {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if let Some(c) = self.plain() {
|
||||
|
|
@ -140,6 +132,9 @@ impl Display for Key {
|
|||
}
|
||||
|
||||
write!(f, "<")?;
|
||||
if self.super_ {
|
||||
write!(f, "D-")?;
|
||||
}
|
||||
if self.ctrl {
|
||||
write!(f, "C-")?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use yazi_shared::Layer;
|
||||
|
||||
use super::Control;
|
||||
use crate::{Preset, MERGED_KEYMAP};
|
||||
use crate::Preset;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Keymap {
|
||||
|
|
@ -14,6 +16,28 @@ pub struct Keymap {
|
|||
pub completion: Vec<Control>,
|
||||
}
|
||||
|
||||
impl Keymap {
|
||||
#[inline]
|
||||
pub fn get(&self, layer: Layer) -> &Vec<Control> {
|
||||
match layer {
|
||||
Layer::App => unreachable!(),
|
||||
Layer::Manager => &self.manager,
|
||||
Layer::Tasks => &self.tasks,
|
||||
Layer::Select => &self.select,
|
||||
Layer::Input => &self.input,
|
||||
Layer::Help => &self.help,
|
||||
Layer::Completion => &self.completion,
|
||||
Layer::Which => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Keymap {
|
||||
type Err = toml::de::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> { toml::from_str(s) }
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Keymap {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
|
|
@ -62,23 +86,3 @@ impl<'de> Deserialize<'de> for Keymap {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Keymap {
|
||||
fn default() -> Self { toml::from_str(&MERGED_KEYMAP).unwrap() }
|
||||
}
|
||||
|
||||
impl Keymap {
|
||||
#[inline]
|
||||
pub fn get(&self, layer: Layer) -> &Vec<Control> {
|
||||
match layer {
|
||||
Layer::App => unreachable!(),
|
||||
Layer::Manager => &self.manager,
|
||||
Layer::Tasks => &self.tasks,
|
||||
Layer::Select => &self.select,
|
||||
Layer::Input => &self.input,
|
||||
Layer::Help => &self.help,
|
||||
Layer::Completion => &self.completion,
|
||||
Layer::Which => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
mod control;
|
||||
mod cow;
|
||||
mod deserializers;
|
||||
mod key;
|
||||
mod keymap;
|
||||
mod run;
|
||||
|
||||
pub use control::*;
|
||||
pub use cow::*;
|
||||
use deserializers::*;
|
||||
pub use key::*;
|
||||
pub use keymap::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use run::*;
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
use std::{fmt, mem};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use serde::{de::{self, Visitor}, Deserializer};
|
||||
use yazi_shared::event::{Cmd, Data};
|
||||
|
||||
pub(super) fn run_deserialize<'de, D>(deserializer: D) -> Result<Vec<Cmd>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct RunVisitor;
|
||||
|
||||
#[allow(clippy::explicit_counter_loop)]
|
||||
fn parse(s: &str) -> Result<Cmd> {
|
||||
let mut args = shell_words::split(s)?;
|
||||
let mut cmd = Cmd { name: mem::take(&mut args[0]), ..Default::default() };
|
||||
|
||||
let mut i = 0usize;
|
||||
for arg in args.into_iter().skip(1) {
|
||||
let Some(arg) = arg.strip_prefix("--") else {
|
||||
cmd.args.insert(i.to_string(), Data::String(arg));
|
||||
i += 1;
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut parts = arg.splitn(2, '=');
|
||||
let Some(key) = parts.next().map(|s| s.to_owned()) else {
|
||||
bail!("invalid argument: {arg}");
|
||||
};
|
||||
|
||||
if let Some(val) = parts.next() {
|
||||
cmd.args.insert(key, Data::String(val.to_owned()));
|
||||
} else {
|
||||
cmd.args.insert(key, Data::Boolean(true));
|
||||
}
|
||||
}
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
impl<'de> Visitor<'de> for RunVisitor {
|
||||
type Value = Vec<Cmd>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a `run` string or array of strings within keymap.toml")
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: de::SeqAccess<'de>,
|
||||
{
|
||||
let mut cmds = vec![];
|
||||
while let Some(value) = &seq.next_element::<String>()? {
|
||||
cmds.push(parse(value).map_err(de::Error::custom)?);
|
||||
}
|
||||
if cmds.is_empty() {
|
||||
return Err(de::Error::custom("`run` within keymap.toml cannot be empty"));
|
||||
}
|
||||
Ok(cmds)
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(vec![parse(value).map_err(de::Error::custom)?])
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(RunVisitor)
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
#![allow(clippy::module_inception)]
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use yazi_shared::{RoCell, Xdg};
|
||||
|
||||
pub mod headsup;
|
||||
pub mod keymap;
|
||||
mod layout;
|
||||
mod log;
|
||||
|
|
@ -16,7 +17,6 @@ pub mod preview;
|
|||
mod priority;
|
||||
mod tasks;
|
||||
pub mod theme;
|
||||
mod validation;
|
||||
pub mod which;
|
||||
|
||||
pub use layout::*;
|
||||
|
|
@ -24,17 +24,8 @@ pub(crate) use pattern::*;
|
|||
pub(crate) use preset::*;
|
||||
pub use priority::*;
|
||||
|
||||
// TODO: remove this once Yazi 0.3 is released --
|
||||
pub static DEPRECATED_EXEC: std::sync::atomic::AtomicBool =
|
||||
std::sync::atomic::AtomicBool::new(false);
|
||||
|
||||
static MERGED_YAZI: RoCell<String> = RoCell::new();
|
||||
static MERGED_KEYMAP: RoCell<String> = RoCell::new();
|
||||
static MERGED_THEME: RoCell<String> = RoCell::new();
|
||||
|
||||
pub static LAYOUT: RoCell<arc_swap::ArcSwap<Layout>> = RoCell::new();
|
||||
|
||||
pub static HEADSUP: RoCell<headsup::Headsup> = RoCell::new();
|
||||
pub static KEYMAP: RoCell<keymap::Keymap> = RoCell::new();
|
||||
pub static LOG: RoCell<log::Log> = RoCell::new();
|
||||
pub static MANAGER: RoCell<manager::Manager> = RoCell::new();
|
||||
|
|
@ -49,37 +40,37 @@ pub static WHICH: RoCell<which::Which> = RoCell::new();
|
|||
|
||||
pub fn init() -> anyhow::Result<()> {
|
||||
let config_dir = Xdg::config_dir();
|
||||
MERGED_YAZI.init(Preset::yazi(&config_dir)?);
|
||||
MERGED_KEYMAP.init(Preset::keymap(&config_dir)?);
|
||||
MERGED_THEME.init(Preset::theme(&config_dir)?);
|
||||
let yazi_toml = &Preset::yazi(&config_dir)?;
|
||||
let keymap_toml = &Preset::keymap(&config_dir)?;
|
||||
let theme_toml = &Preset::theme(&config_dir)?;
|
||||
|
||||
LAYOUT.with(Default::default);
|
||||
|
||||
HEADSUP.with(Default::default);
|
||||
KEYMAP.with(Default::default);
|
||||
LOG.with(Default::default);
|
||||
MANAGER.with(Default::default);
|
||||
OPEN.with(Default::default);
|
||||
PLUGIN.with(Default::default);
|
||||
PREVIEW.with(Default::default);
|
||||
TASKS.with(Default::default);
|
||||
THEME.with(Default::default);
|
||||
INPUT.with(Default::default);
|
||||
SELECT.with(Default::default);
|
||||
WHICH.with(Default::default);
|
||||
KEYMAP.init(<_>::from_str(keymap_toml)?);
|
||||
LOG.init(<_>::from_str(yazi_toml)?);
|
||||
MANAGER.init(<_>::from_str(yazi_toml)?);
|
||||
OPEN.init(<_>::from_str(yazi_toml)?);
|
||||
PLUGIN.init(<_>::from_str(yazi_toml)?);
|
||||
PREVIEW.init(<_>::from_str(yazi_toml)?);
|
||||
TASKS.init(<_>::from_str(yazi_toml)?);
|
||||
THEME.init(<_>::from_str(theme_toml)?);
|
||||
INPUT.init(<_>::from_str(yazi_toml)?);
|
||||
SELECT.init(<_>::from_str(yazi_toml)?);
|
||||
WHICH.init(<_>::from_str(yazi_toml)?);
|
||||
|
||||
// TODO: remove this once Yazi 0.3 is released --
|
||||
if !HEADSUP.disable_exec_warn && DEPRECATED_EXEC.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
// TODO: Remove in v0.3.2
|
||||
for c in &KEYMAP.manager {
|
||||
for r in &c.run {
|
||||
if r.name == "shell" && !r.bool("confirm") && !r.bool("interactive") {
|
||||
eprintln!(
|
||||
r#"
|
||||
WARNING: `exec` will be deprecated in the next major version v0.3 and replaced by `run`.
|
||||
r#"WARNING: In Yazi v0.3, the behavior of the interactive `shell` (i.e., shell templates) must be explicitly specified with `--interactive`.
|
||||
|
||||
Please replace all `exec = ...` with `run = ...`, in your `yazi.toml` and `keymap.toml`.
|
||||
|
||||
---
|
||||
Add `disable_exec_warn = true` to your `yazi.toml` under `[headsup]` to suppress this warning.
|
||||
"#
|
||||
Please replace e.g. `shell` with `shell --interactive`, `shell "my-template"` with `shell "my-template" --interactive`, in your keymap.toml"#
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
use serde::{Deserialize, Deserializer};
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::MERGED_YAZI;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Log {
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl Default for Log {
|
||||
fn default() -> Self { toml::from_str(&MERGED_YAZI).unwrap() }
|
||||
impl FromStr for Log {
|
||||
type Err = toml::de::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> { toml::from_str(s) }
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Log {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
|
||||
use super::{ManagerRatio, SortBy};
|
||||
use crate::{validation::check_validation, MERGED_YAZI};
|
||||
use super::{ManagerRatio, MouseEvents, SortBy};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Validate)]
|
||||
pub struct Manager {
|
||||
|
|
@ -13,6 +14,7 @@ pub struct Manager {
|
|||
pub sort_sensitive: bool,
|
||||
pub sort_reverse: bool,
|
||||
pub sort_dir_first: bool,
|
||||
pub sort_translit: bool,
|
||||
|
||||
// Display
|
||||
#[validate(length(min = 1, max = 20, message = "must be between 1 and 20 characters"))]
|
||||
|
|
@ -20,18 +22,21 @@ pub struct Manager {
|
|||
pub show_hidden: bool,
|
||||
pub show_symlink: bool,
|
||||
pub scrolloff: u8,
|
||||
pub mouse_events: MouseEvents,
|
||||
}
|
||||
|
||||
impl Default for Manager {
|
||||
fn default() -> Self {
|
||||
impl FromStr for Manager {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
manager: Manager,
|
||||
}
|
||||
|
||||
let manager = toml::from_str::<Outer>(&MERGED_YAZI).unwrap().manager;
|
||||
let manager = toml::from_str::<Outer>(s)?.manager;
|
||||
manager.validate()?;
|
||||
|
||||
check_validation(manager.validate());
|
||||
manager
|
||||
Ok(manager)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
mod manager;
|
||||
mod mouse;
|
||||
mod ratio;
|
||||
mod sorting;
|
||||
|
||||
pub use manager::*;
|
||||
pub use mouse::*;
|
||||
pub use ratio::*;
|
||||
pub use sorting::*;
|
||||
|
|
|
|||
62
yazi-config/src/manager/mouse.rs
Normal file
62
yazi-config/src/manager/mouse.rs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
use anyhow::{bail, Result};
|
||||
use bitflags::bitflags;
|
||||
use crossterm::event::MouseEventKind;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
|
||||
#[serde(try_from = "Vec<String>", into = "Vec<String>")]
|
||||
pub struct MouseEvents: u8 {
|
||||
const CLICK = 0b00001;
|
||||
const SCROLL = 0b00010;
|
||||
const TOUCH = 0b00100;
|
||||
const MOVE = 0b01000;
|
||||
const DRAG = 0b10000;
|
||||
}
|
||||
}
|
||||
|
||||
impl MouseEvents {
|
||||
pub const fn draggable(self) -> bool { self.contains(Self::DRAG) }
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<String>> for MouseEvents {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: Vec<String>) -> Result<Self, Self::Error> {
|
||||
value.into_iter().try_fold(Self::empty(), |aac, s| {
|
||||
Ok(match s.as_str() {
|
||||
"click" => aac | Self::CLICK,
|
||||
"scroll" => aac | Self::SCROLL,
|
||||
"touch" => aac | Self::TOUCH,
|
||||
"move" => aac | Self::MOVE,
|
||||
"drag" => aac | Self::DRAG,
|
||||
_ => bail!("Invalid mouse event: {s}"),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MouseEvents> for Vec<String> {
|
||||
fn from(value: MouseEvents) -> Self {
|
||||
let events = [
|
||||
(MouseEvents::CLICK, "click"),
|
||||
(MouseEvents::SCROLL, "scroll"),
|
||||
(MouseEvents::TOUCH, "touch"),
|
||||
(MouseEvents::MOVE, "move"),
|
||||
(MouseEvents::DRAG, "drag"),
|
||||
];
|
||||
events.into_iter().filter(|v| value.contains(v.0)).map(|v| v.1.to_owned()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crossterm::event::MouseEventKind> for MouseEvents {
|
||||
fn from(value: crossterm::event::MouseEventKind) -> Self {
|
||||
match value {
|
||||
MouseEventKind::Down(_) | MouseEventKind::Up(_) => Self::CLICK,
|
||||
MouseEventKind::ScrollDown | MouseEventKind::ScrollUp => Self::SCROLL,
|
||||
MouseEventKind::ScrollLeft | MouseEventKind::ScrollRight => Self::TOUCH,
|
||||
MouseEventKind::Moved => Self::MOVE,
|
||||
MouseEventKind::Drag(_) => Self::DRAG,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
use std::{collections::HashMap, path::Path};
|
||||
use std::{collections::HashMap, path::Path, str::FromStr};
|
||||
|
||||
use indexmap::IndexSet;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use yazi_shared::MIME_DIR;
|
||||
|
||||
use super::Opener;
|
||||
use crate::{open::OpenRule, Preset, MERGED_YAZI};
|
||||
use crate::{open::OpenRule, Preset};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Open {
|
||||
|
|
@ -13,20 +13,16 @@ pub struct Open {
|
|||
openers: HashMap<String, IndexSet<Opener>>,
|
||||
}
|
||||
|
||||
impl Default for Open {
|
||||
fn default() -> Self { toml::from_str(&MERGED_YAZI).unwrap() }
|
||||
}
|
||||
|
||||
impl Open {
|
||||
pub fn openers<P, M>(&self, path: P, mime: M) -> Option<IndexSet<&Opener>>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
M: AsRef<str>,
|
||||
{
|
||||
let is_folder = mime.as_ref() == MIME_DIR;
|
||||
let is_dir = mime.as_ref() == MIME_DIR;
|
||||
self.rules.iter().find_map(|rule| {
|
||||
if rule.mime.as_ref().is_some_and(|p| p.match_mime(&mime))
|
||||
|| rule.name.as_ref().is_some_and(|p| p.match_path(&path, is_folder))
|
||||
|| rule.name.as_ref().is_some_and(|p| p.match_path(&path, is_dir))
|
||||
{
|
||||
let openers = rule
|
||||
.use_
|
||||
|
|
@ -58,6 +54,12 @@ impl Open {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Open {
|
||||
type Err = toml::de::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> { toml::from_str(s) }
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Open {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
|
|
@ -78,6 +80,13 @@ impl<'de> Deserialize<'de> for Open {
|
|||
}
|
||||
|
||||
let mut outer = Outer::deserialize(deserializer)?;
|
||||
|
||||
if outer.open.append_rules.iter().any(|r| r.any_file()) {
|
||||
outer.open.rules.retain(|r| !r.any_file());
|
||||
}
|
||||
if outer.open.append_rules.iter().any(|r| r.any_dir()) {
|
||||
outer.open.rules.retain(|r| !r.any_dir());
|
||||
}
|
||||
Preset::mix(&mut outer.open.rules, outer.open.prepend_rules, outer.open.append_rules);
|
||||
|
||||
let openers = outer
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
use std::sync::atomic::Ordering;
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
use crate::DEPRECATED_EXEC;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Opener {
|
||||
pub run: String,
|
||||
|
|
@ -36,9 +32,7 @@ impl<'de> Deserialize<'de> for Opener {
|
|||
{
|
||||
#[derive(Deserialize)]
|
||||
pub struct Shadow {
|
||||
run: Option<String>,
|
||||
// TODO: remove this once Yazi 0.3 is released --
|
||||
exec: Option<String>,
|
||||
run: String,
|
||||
#[serde(default)]
|
||||
block: bool,
|
||||
#[serde(default)]
|
||||
|
|
@ -50,13 +44,7 @@ impl<'de> Deserialize<'de> for Opener {
|
|||
|
||||
let shadow = Shadow::deserialize(deserializer)?;
|
||||
|
||||
// TODO: remove this once Yazi 0.3 is released --
|
||||
if shadow.exec.is_some() {
|
||||
DEPRECATED_EXEC.store(true, Ordering::Relaxed);
|
||||
}
|
||||
let run = shadow.run.or(shadow.exec).unwrap_or_default();
|
||||
// TODO: -- remove this once Yazi 0.3 is released
|
||||
|
||||
let run = shadow.run;
|
||||
if run.is_empty() {
|
||||
return Err(serde::de::Error::custom("`run` cannot be empty"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,14 @@ pub(super) struct OpenRule {
|
|||
pub(super) use_: Vec<String>,
|
||||
}
|
||||
|
||||
impl OpenRule {
|
||||
#[inline]
|
||||
pub fn any_file(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_file()) }
|
||||
|
||||
#[inline]
|
||||
pub fn any_dir(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_dir()) }
|
||||
}
|
||||
|
||||
impl OpenRule {
|
||||
fn deserialize<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
|
||||
where
|
||||
|
|
|
|||
|
|
@ -7,24 +7,26 @@ use serde::Deserialize;
|
|||
#[serde(try_from = "String")]
|
||||
pub struct Pattern {
|
||||
inner: globset::GlobMatcher,
|
||||
is_dir: bool,
|
||||
is_star: bool,
|
||||
is_folder: bool,
|
||||
}
|
||||
|
||||
impl Pattern {
|
||||
#[inline]
|
||||
pub fn match_mime(&self, str: impl AsRef<str>) -> bool { self.inner.is_match(str.as_ref()) }
|
||||
|
||||
#[inline]
|
||||
pub fn match_path(&self, path: impl AsRef<Path>, is_folder: bool) -> bool {
|
||||
is_folder == self.is_folder && (self.is_star || self.inner.is_match(path))
|
||||
pub fn match_mime(&self, str: impl AsRef<str>) -> bool {
|
||||
self.is_star || self.inner.is_match(str.as_ref())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn any_file(&self) -> bool { self.is_star && !self.is_folder }
|
||||
pub fn match_path(&self, path: impl AsRef<Path>, is_dir: bool) -> bool {
|
||||
is_dir == self.is_dir && (self.is_star || self.inner.is_match(path))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn any_dir(&self) -> bool { self.is_star && self.is_folder }
|
||||
pub fn any_file(&self) -> bool { self.is_star && !self.is_dir }
|
||||
|
||||
#[inline]
|
||||
pub fn any_dir(&self) -> bool { self.is_star && self.is_dir }
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Pattern {
|
||||
|
|
@ -36,13 +38,13 @@ impl TryFrom<&str> for Pattern {
|
|||
|
||||
let inner = GlobBuilder::new(b)
|
||||
.case_insensitive(a.len() == s.len())
|
||||
.literal_separator(b.contains('/'))
|
||||
.literal_separator(false)
|
||||
.backslash_escape(false)
|
||||
.empty_alternates(false)
|
||||
.empty_alternates(true)
|
||||
.build()?
|
||||
.compile_matcher();
|
||||
|
||||
Ok(Self { inner, is_star: b == "*", is_folder: b.len() < a.len() })
|
||||
Ok(Self { inner, is_dir: b.len() < a.len(), is_star: b == "*" })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
32
yazi-config/src/plugin/fetcher.rs
Normal file
32
yazi-config/src/plugin/fetcher.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
use serde::Deserialize;
|
||||
use yazi_shared::{event::Cmd, Condition};
|
||||
|
||||
use crate::{Pattern, Priority};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Fetcher {
|
||||
#[serde(skip)]
|
||||
pub idx: u8,
|
||||
|
||||
pub id: String,
|
||||
#[serde(rename = "if")]
|
||||
pub if_: Option<Condition>,
|
||||
pub name: Option<Pattern>,
|
||||
pub mime: Option<Pattern>,
|
||||
pub run: Cmd,
|
||||
#[serde(default)]
|
||||
pub prio: Priority,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FetcherProps {
|
||||
pub id: u8,
|
||||
pub name: String,
|
||||
pub prio: Priority,
|
||||
}
|
||||
|
||||
impl From<&Fetcher> for FetcherProps {
|
||||
fn from(fetcher: &Fetcher) -> Self {
|
||||
Self { id: fetcher.idx, name: fetcher.run.name.to_owned(), prio: fetcher.prio }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
mod fetcher;
|
||||
mod plugin;
|
||||
mod props;
|
||||
mod rule;
|
||||
mod run;
|
||||
mod preloader;
|
||||
mod previewer;
|
||||
|
||||
pub use fetcher::*;
|
||||
pub use plugin::*;
|
||||
pub use props::*;
|
||||
pub use rule::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use run::*;
|
||||
pub use preloader::*;
|
||||
pub use previewer::*;
|
||||
|
||||
pub const MAX_PRELOADERS: u8 = 32;
|
||||
pub const MAX_PREWORKERS: u8 = 32;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,68 @@
|
|||
use std::path::Path;
|
||||
use std::{path::Path, str::FromStr};
|
||||
|
||||
use serde::Deserialize;
|
||||
use yazi_shared::MIME_DIR;
|
||||
|
||||
use super::PluginRule;
|
||||
use crate::{plugin::MAX_PRELOADERS, Preset, MERGED_YAZI};
|
||||
use super::{Fetcher, Preloader, Previewer};
|
||||
use crate::{plugin::MAX_PREWORKERS, Preset};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Plugin {
|
||||
pub preloaders: Vec<PluginRule>,
|
||||
pub previewers: Vec<PluginRule>,
|
||||
pub fetchers: Vec<Fetcher>,
|
||||
pub preloaders: Vec<Preloader>,
|
||||
pub previewers: Vec<Previewer>,
|
||||
}
|
||||
|
||||
impl Default for Plugin {
|
||||
fn default() -> Self {
|
||||
impl Plugin {
|
||||
pub fn fetchers(
|
||||
&self,
|
||||
path: &Path,
|
||||
mime: Option<&str>,
|
||||
f: impl Fn(&str) -> bool + Copy,
|
||||
) -> Vec<&Fetcher> {
|
||||
let is_dir = mime == Some(MIME_DIR);
|
||||
self
|
||||
.fetchers
|
||||
.iter()
|
||||
.filter(|&p| {
|
||||
p.if_.as_ref().and_then(|c| c.eval(f)) != Some(false)
|
||||
&& (p.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m))
|
||||
|| p.name.as_ref().is_some_and(|p| p.match_path(path, is_dir)))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn preloaders(&self, path: &Path, mime: Option<&str>) -> Vec<&Preloader> {
|
||||
let is_dir = mime == Some(MIME_DIR);
|
||||
let mut preloaders = Vec::with_capacity(1);
|
||||
|
||||
for p in &self.preloaders {
|
||||
if !p.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m))
|
||||
&& !p.name.as_ref().is_some_and(|p| p.match_path(path, is_dir))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
preloaders.push(p);
|
||||
if !p.next {
|
||||
break;
|
||||
}
|
||||
}
|
||||
preloaders
|
||||
}
|
||||
|
||||
pub fn previewer(&self, path: &Path, mime: &str) -> Option<&Previewer> {
|
||||
let is_dir = mime == MIME_DIR;
|
||||
self.previewers.iter().find(|&p| {
|
||||
p.mime.as_ref().is_some_and(|p| p.match_mime(mime))
|
||||
|| p.name.as_ref().is_some_and(|p| p.match_path(path, is_dir))
|
||||
})
|
||||
}
|
||||
}
|
||||
impl FromStr for Plugin {
|
||||
type Err = toml::de::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
plugin: Shadow,
|
||||
|
|
@ -21,20 +70,26 @@ impl Default for Plugin {
|
|||
|
||||
#[derive(Deserialize)]
|
||||
struct Shadow {
|
||||
preloaders: Vec<PluginRule>,
|
||||
fetchers: Vec<Fetcher>,
|
||||
#[serde(default)]
|
||||
prepend_preloaders: Vec<PluginRule>,
|
||||
prepend_fetchers: Vec<Fetcher>,
|
||||
#[serde(default)]
|
||||
append_preloaders: Vec<PluginRule>,
|
||||
append_fetchers: Vec<Fetcher>,
|
||||
|
||||
previewers: Vec<PluginRule>,
|
||||
preloaders: Vec<Preloader>,
|
||||
#[serde(default)]
|
||||
prepend_previewers: Vec<PluginRule>,
|
||||
prepend_preloaders: Vec<Preloader>,
|
||||
#[serde(default)]
|
||||
append_previewers: Vec<PluginRule>,
|
||||
append_preloaders: Vec<Preloader>,
|
||||
|
||||
previewers: Vec<Previewer>,
|
||||
#[serde(default)]
|
||||
prepend_previewers: Vec<Previewer>,
|
||||
#[serde(default)]
|
||||
append_previewers: Vec<Previewer>,
|
||||
}
|
||||
|
||||
let mut shadow = toml::from_str::<Outer>(&MERGED_YAZI).unwrap().plugin;
|
||||
let mut shadow = toml::from_str::<Outer>(s)?.plugin;
|
||||
if shadow.append_previewers.iter().any(|r| r.any_file()) {
|
||||
shadow.previewers.retain(|r| !r.any_file());
|
||||
}
|
||||
|
|
@ -42,48 +97,25 @@ impl Default for Plugin {
|
|||
shadow.previewers.retain(|r| !r.any_dir());
|
||||
}
|
||||
|
||||
Preset::mix(&mut shadow.fetchers, shadow.prepend_fetchers, shadow.append_fetchers);
|
||||
Preset::mix(&mut shadow.preloaders, shadow.prepend_preloaders, shadow.append_preloaders);
|
||||
Preset::mix(&mut shadow.previewers, shadow.prepend_previewers, shadow.append_previewers);
|
||||
|
||||
if shadow.preloaders.len() > MAX_PRELOADERS as usize {
|
||||
panic!("Too many preloaders");
|
||||
if shadow.fetchers.len() + shadow.preloaders.len() > MAX_PREWORKERS as usize {
|
||||
panic!("Fetchers and preloaders exceed the limit of {MAX_PREWORKERS}");
|
||||
}
|
||||
|
||||
for (i, preloader) in shadow.preloaders.iter_mut().enumerate() {
|
||||
if preloader.sync {
|
||||
panic!("Preloaders cannot be synchronous");
|
||||
for (i, p) in shadow.fetchers.iter_mut().enumerate() {
|
||||
p.idx = i as u8;
|
||||
}
|
||||
preloader.id = i as u8;
|
||||
for (i, p) in shadow.preloaders.iter_mut().enumerate() {
|
||||
p.idx = shadow.fetchers.len() as u8 + i as u8;
|
||||
}
|
||||
|
||||
Self { preloaders: shadow.preloaders, previewers: shadow.previewers }
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin {
|
||||
pub fn preloaders(
|
||||
&self,
|
||||
path: &Path,
|
||||
mime: Option<&str>,
|
||||
f: impl Fn(&str) -> bool + Copy,
|
||||
) -> Vec<&PluginRule> {
|
||||
let is_folder = mime == Some(MIME_DIR);
|
||||
self
|
||||
.preloaders
|
||||
.iter()
|
||||
.filter(|&rule| {
|
||||
rule.cond.as_ref().and_then(|c| c.eval(f)) != Some(false)
|
||||
&& (rule.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m))
|
||||
|| rule.name.as_ref().is_some_and(|p| p.match_path(path, is_folder)))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn previewer(&self, path: &Path, mime: &str) -> Option<&PluginRule> {
|
||||
let is_folder = mime == MIME_DIR;
|
||||
self.previewers.iter().find(|&rule| {
|
||||
rule.mime.as_ref().is_some_and(|p| p.match_mime(mime))
|
||||
|| rule.name.as_ref().is_some_and(|p| p.match_path(path, is_folder))
|
||||
Ok(Self {
|
||||
fetchers: shadow.fetchers,
|
||||
preloaders: shadow.preloaders,
|
||||
previewers: shadow.previewers,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
31
yazi-config/src/plugin/preloader.rs
Normal file
31
yazi-config/src/plugin/preloader.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use serde::Deserialize;
|
||||
use yazi_shared::event::Cmd;
|
||||
|
||||
use crate::{Pattern, Priority};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Preloader {
|
||||
#[serde(skip)]
|
||||
pub idx: u8,
|
||||
|
||||
pub name: Option<Pattern>,
|
||||
pub mime: Option<Pattern>,
|
||||
pub run: Cmd,
|
||||
#[serde(default)]
|
||||
pub next: bool,
|
||||
#[serde(default)]
|
||||
pub prio: Priority,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PreloaderProps {
|
||||
pub id: u8,
|
||||
pub name: String,
|
||||
pub prio: Priority,
|
||||
}
|
||||
|
||||
impl From<&Preloader> for PreloaderProps {
|
||||
fn from(preloader: &Preloader) -> Self {
|
||||
Self { id: preloader.idx, name: preloader.run.name.to_owned(), prio: preloader.prio }
|
||||
}
|
||||
}
|
||||
21
yazi-config/src/plugin/previewer.rs
Normal file
21
yazi-config/src/plugin/previewer.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
use serde::Deserialize;
|
||||
use yazi_shared::event::Cmd;
|
||||
|
||||
use crate::Pattern;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Previewer {
|
||||
pub name: Option<Pattern>,
|
||||
pub mime: Option<Pattern>,
|
||||
pub run: Cmd,
|
||||
#[serde(default)]
|
||||
pub sync: bool,
|
||||
}
|
||||
|
||||
impl Previewer {
|
||||
#[inline]
|
||||
pub fn any_file(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_file()) }
|
||||
|
||||
#[inline]
|
||||
pub fn any_dir(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_dir()) }
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
use super::PluginRule;
|
||||
use crate::Priority;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PluginProps {
|
||||
pub id: u8,
|
||||
pub name: String,
|
||||
pub multi: bool,
|
||||
pub prio: Priority,
|
||||
}
|
||||
|
||||
impl From<&PluginRule> for PluginProps {
|
||||
fn from(rule: &PluginRule) -> Self {
|
||||
Self { id: rule.id, name: rule.cmd.name.to_owned(), multi: rule.multi, prio: rule.prio }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
use std::sync::atomic::Ordering;
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use yazi_shared::{event::Cmd, Condition};
|
||||
|
||||
use crate::{Pattern, Priority, DEPRECATED_EXEC};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PluginRule {
|
||||
pub id: u8,
|
||||
pub cond: Option<Condition>,
|
||||
pub name: Option<Pattern>,
|
||||
pub mime: Option<Pattern>,
|
||||
pub cmd: Cmd,
|
||||
pub sync: bool,
|
||||
pub multi: bool,
|
||||
pub prio: Priority,
|
||||
}
|
||||
|
||||
impl PluginRule {
|
||||
#[inline]
|
||||
pub fn any_file(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_file()) }
|
||||
|
||||
#[inline]
|
||||
pub fn any_dir(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_dir()) }
|
||||
}
|
||||
|
||||
// TODO: remove this once Yazi 0.3 is released
|
||||
impl<'de> Deserialize<'de> for PluginRule {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
pub struct Shadow {
|
||||
#[serde(default)]
|
||||
pub id: u8,
|
||||
pub cond: Option<Condition>,
|
||||
pub name: Option<Pattern>,
|
||||
pub mime: Option<Pattern>,
|
||||
pub run: Option<WrappedCmd>,
|
||||
pub exec: Option<WrappedCmd>,
|
||||
#[serde(default)]
|
||||
pub sync: bool,
|
||||
#[serde(default)]
|
||||
pub multi: bool,
|
||||
#[serde(default)]
|
||||
pub prio: Priority,
|
||||
}
|
||||
|
||||
let shadow = Shadow::deserialize(deserializer)?;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct WrappedCmd(#[serde(deserialize_with = "super::run_deserialize")] Cmd);
|
||||
|
||||
if shadow.exec.is_some() {
|
||||
DEPRECATED_EXEC.store(true, Ordering::Relaxed);
|
||||
}
|
||||
let Some(run) = shadow.run.or(shadow.exec) else {
|
||||
return Err(serde::de::Error::custom("missing field `run` within `[plugin]`"));
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
id: shadow.id,
|
||||
cond: shadow.cond,
|
||||
name: shadow.name,
|
||||
mime: shadow.mime,
|
||||
cmd: run.0,
|
||||
sync: shadow.sync,
|
||||
multi: shadow.multi,
|
||||
prio: shadow.prio,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
use std::fmt;
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::{de::{self, Visitor}, Deserializer};
|
||||
use yazi_shared::event::Cmd;
|
||||
|
||||
pub(super) fn run_deserialize<'de, D>(deserializer: D) -> Result<Cmd, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct RunVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for RunVisitor {
|
||||
type Value = Cmd;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a `run` string or array of strings")
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, _: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: de::SeqAccess<'de>,
|
||||
{
|
||||
Err(de::Error::custom("`run` within [plugin] must be a string"))
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
if value.is_empty() {
|
||||
return Err(de::Error::custom("`run` within [plugin] cannot be empty"));
|
||||
}
|
||||
Ok(Cmd { name: value.to_owned(), ..Default::default() })
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(RunVisitor)
|
||||
}
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::{Offset, Origin};
|
||||
use crate::MERGED_YAZI;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Input {
|
||||
pub cursor_blink: bool,
|
||||
|
||||
// cd
|
||||
pub cd_title: String,
|
||||
pub cd_origin: Origin,
|
||||
|
|
@ -61,18 +64,19 @@ pub struct Input {
|
|||
pub quit_offset: Offset,
|
||||
}
|
||||
|
||||
impl Default for Input {
|
||||
fn default() -> Self {
|
||||
impl Input {
|
||||
pub const fn border(&self) -> u16 { 2 }
|
||||
}
|
||||
|
||||
impl FromStr for Input {
|
||||
type Err = toml::de::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
input: Input,
|
||||
}
|
||||
|
||||
toml::from_str::<Outer>(&MERGED_YAZI).unwrap().input
|
||||
Ok(toml::from_str::<Outer>(s)?.input)
|
||||
}
|
||||
}
|
||||
|
||||
impl Input {
|
||||
#[inline]
|
||||
pub const fn border(&self) -> u16 { 2 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use crossterm::terminal::WindowSize;
|
||||
use ratatui::layout::Rect;
|
||||
use yazi_shared::term::Term;
|
||||
|
||||
use super::{Offset, Origin};
|
||||
|
||||
|
|
@ -14,10 +13,9 @@ impl Position {
|
|||
#[inline]
|
||||
pub fn new(origin: Origin, offset: Offset) -> Self { Self { origin, offset } }
|
||||
|
||||
pub fn rect(&self) -> Rect {
|
||||
pub fn rect(&self, WindowSize { columns, rows, .. }: WindowSize) -> Rect {
|
||||
use Origin::*;
|
||||
let Offset { x, y, width, height } = self.offset;
|
||||
let WindowSize { columns, rows, .. } = Term::size();
|
||||
|
||||
let max_x = columns.saturating_sub(width);
|
||||
let new_x = match self.origin {
|
||||
|
|
@ -45,9 +43,8 @@ impl Position {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn sticky(base: Rect, offset: Offset) -> Rect {
|
||||
pub fn sticky(WindowSize { columns, rows, .. }: WindowSize, base: Rect, offset: Offset) -> Rect {
|
||||
let Offset { x, y, width, height } = offset;
|
||||
let WindowSize { columns, rows, .. } = Term::size();
|
||||
|
||||
let above =
|
||||
base.y.saturating_add(base.height).saturating_add(height).saturating_add_signed(y) > rows;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::{Offset, Origin};
|
||||
use crate::MERGED_YAZI;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Select {
|
||||
|
|
@ -11,18 +12,19 @@ pub struct Select {
|
|||
pub open_offset: Offset,
|
||||
}
|
||||
|
||||
impl Default for Select {
|
||||
fn default() -> Self {
|
||||
impl Select {
|
||||
pub const fn border(&self) -> u16 { 2 }
|
||||
}
|
||||
|
||||
impl FromStr for Select {
|
||||
type Err = toml::de::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
select: Select,
|
||||
}
|
||||
|
||||
toml::from_str::<Outer>(&MERGED_YAZI).unwrap().select
|
||||
Ok(toml::from_str::<Outer>(s)?.select)
|
||||
}
|
||||
}
|
||||
|
||||
impl Select {
|
||||
#[inline]
|
||||
pub const fn border(&self) -> u16 { 2 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{mem, path::{Path, PathBuf}};
|
||||
use std::{borrow::Cow, mem, path::{Path, PathBuf}};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use toml::{Table, Value};
|
||||
|
|
@ -8,17 +8,17 @@ use crate::theme::Flavor;
|
|||
pub(crate) struct Preset;
|
||||
|
||||
impl Preset {
|
||||
pub(crate) fn yazi(p: &Path) -> Result<String> {
|
||||
pub(crate) fn yazi(p: &Path) -> Result<Cow<str>> {
|
||||
Self::merge_path(p.join("yazi.toml"), include_str!("../preset/yazi.toml"))
|
||||
}
|
||||
|
||||
pub(crate) fn keymap(p: &Path) -> Result<String> {
|
||||
pub(crate) fn keymap(p: &Path) -> Result<Cow<str>> {
|
||||
Self::merge_path(p.join("keymap.toml"), include_str!("../preset/keymap.toml"))
|
||||
}
|
||||
|
||||
pub(crate) fn theme(p: &Path) -> Result<String> {
|
||||
pub(crate) fn theme(p: &Path) -> Result<Cow<str>> {
|
||||
let Ok(user) = std::fs::read_to_string(p.join("theme.toml")) else {
|
||||
return Ok(include_str!("../preset/theme.toml").to_owned());
|
||||
return Ok(include_str!("../preset/theme.toml").into());
|
||||
};
|
||||
let Some(use_) = Flavor::parse_use(&user) else {
|
||||
return Self::merge_str(&user, include_str!("../preset/theme.toml"));
|
||||
|
|
@ -37,18 +37,18 @@ impl Preset {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn merge_str(user: &str, base: &str) -> Result<String> {
|
||||
pub(crate) fn merge_str(user: &str, base: &str) -> Result<Cow<'static, str>> {
|
||||
let mut t = user.parse()?;
|
||||
Self::merge(&mut t, base.parse()?, 2);
|
||||
|
||||
Ok(t.to_string())
|
||||
Ok(t.to_string().into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn merge_path(user: PathBuf, base: &str) -> Result<String> {
|
||||
fn merge_path(user: PathBuf, base: &str) -> Result<Cow<str>> {
|
||||
let s = std::fs::read_to_string(&user).unwrap_or_default();
|
||||
if s.is_empty() {
|
||||
return Ok(base.to_string());
|
||||
return Ok(base.into());
|
||||
}
|
||||
|
||||
Self::merge_str(&s, base).with_context(|| anyhow!("Loading {user:?}"))
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use std::{path::PathBuf, time::{self, SystemTime}};
|
||||
use std::{path::PathBuf, str::FromStr, time::{self, SystemTime}};
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
use yazi_shared::fs::expand_path;
|
||||
|
||||
use crate::{validation::check_validation, Xdg, MERGED_YAZI};
|
||||
use crate::Xdg;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Preview {
|
||||
|
|
@ -22,8 +23,18 @@ pub struct Preview {
|
|||
pub ueberzug_offset: (f32, f32, f32, f32),
|
||||
}
|
||||
|
||||
impl Default for Preview {
|
||||
fn default() -> Self {
|
||||
impl Preview {
|
||||
#[inline]
|
||||
pub fn tmpfile(&self, prefix: &str) -> PathBuf {
|
||||
let nanos = SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos();
|
||||
self.cache_dir.join(format!("{prefix}-{}", nanos / 1000))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Preview {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
preview: Shadow,
|
||||
|
|
@ -46,17 +57,14 @@ impl Default for Preview {
|
|||
ueberzug_offset: (f32, f32, f32, f32),
|
||||
}
|
||||
|
||||
let preview = toml::from_str::<Outer>(&MERGED_YAZI).unwrap().preview;
|
||||
check_validation(preview.validate());
|
||||
let preview = toml::from_str::<Outer>(s)?.preview;
|
||||
preview.validate()?;
|
||||
|
||||
let cache_dir =
|
||||
preview.cache_dir.filter(|p| !p.is_empty()).map_or_else(Xdg::cache_dir, expand_path);
|
||||
std::fs::create_dir_all(&cache_dir).context("Failed to create cache directory")?;
|
||||
|
||||
if !cache_dir.is_dir() {
|
||||
std::fs::create_dir(&cache_dir).expect("Failed to create cache directory");
|
||||
}
|
||||
|
||||
Preview {
|
||||
Ok(Preview {
|
||||
tab_size: preview.tab_size,
|
||||
max_width: preview.max_width,
|
||||
max_height: preview.max_height,
|
||||
|
|
@ -69,14 +77,6 @@ impl Default for Preview {
|
|||
|
||||
ueberzug_scale: preview.ueberzug_scale,
|
||||
ueberzug_offset: preview.ueberzug_offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Preview {
|
||||
#[inline]
|
||||
pub fn tmpfile(&self, prefix: &str) -> PathBuf {
|
||||
let nanos = SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos();
|
||||
self.cache_dir.join(format!("{prefix}-{}", nanos / 1000))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use serde::Deserialize;
|
||||
use validator::Validate;
|
||||
|
||||
use crate::{validation::check_validation, MERGED_YAZI};
|
||||
|
||||
#[derive(Debug, Deserialize, Validate)]
|
||||
pub struct Tasks {
|
||||
#[validate(range(min = 1, message = "Cannot be less than 1"))]
|
||||
|
|
@ -18,16 +18,18 @@ pub struct Tasks {
|
|||
pub suppress_preload: bool,
|
||||
}
|
||||
|
||||
impl Default for Tasks {
|
||||
fn default() -> Self {
|
||||
impl FromStr for Tasks {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
tasks: Tasks,
|
||||
}
|
||||
|
||||
let tasks = toml::from_str::<Outer>(&MERGED_YAZI).unwrap().tasks;
|
||||
check_validation(tasks.validate());
|
||||
let tasks = toml::from_str::<Outer>(s)?.tasks;
|
||||
tasks.validate()?;
|
||||
|
||||
tasks
|
||||
Ok(tasks)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use yazi_shared::{fs::File, theme::{Color, StyleShadow}};
|
||||
|
||||
use crate::{preset::Preset, theme::Is, Pattern};
|
||||
|
||||
pub struct Icon {
|
||||
is: Is,
|
||||
name: Pattern,
|
||||
inner: yazi_shared::theme::Icon,
|
||||
}
|
||||
|
||||
impl Deref for Icon {
|
||||
type Target = yazi_shared::theme::Icon;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
pub fn matches(&self, file: &File) -> bool {
|
||||
if !self.is.check(&file.cha) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.name.match_path(&file.url, file.is_dir())
|
||||
}
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Icon>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct IconOuter {
|
||||
rules: Vec<IconRule>,
|
||||
#[serde(default)]
|
||||
prepend_rules: Vec<IconRule>,
|
||||
#[serde(default)]
|
||||
append_rules: Vec<IconRule>,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
struct IconRule {
|
||||
#[serde(default)]
|
||||
is: Is,
|
||||
name: Pattern,
|
||||
text: String,
|
||||
|
||||
fg: Option<Color>,
|
||||
}
|
||||
|
||||
let mut outer = IconOuter::deserialize(deserializer)?;
|
||||
if outer.append_rules.iter().any(|r| r.name.any_file()) {
|
||||
outer.rules.retain(|r| !r.name.any_file());
|
||||
}
|
||||
if outer.append_rules.iter().any(|r| r.name.any_dir()) {
|
||||
outer.rules.retain(|r| !r.name.any_dir());
|
||||
}
|
||||
|
||||
Preset::mix(&mut outer.rules, outer.prepend_rules, outer.append_rules);
|
||||
|
||||
Ok(
|
||||
outer
|
||||
.rules
|
||||
.into_iter()
|
||||
.map(|r| Icon {
|
||||
is: r.is,
|
||||
name: r.name,
|
||||
inner: yazi_shared::theme::Icon {
|
||||
text: r.text,
|
||||
style: StyleShadow { fg: r.fg, ..Default::default() }.into(),
|
||||
},
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
169
yazi-config/src/theme/icons.rs
Normal file
169
yazi-config/src/theme/icons.rs
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use yazi_shared::{fs::File, theme::{Color, Icon, Style}, Condition};
|
||||
|
||||
use crate::{Pattern, Preset};
|
||||
|
||||
pub struct Icons {
|
||||
globs: Vec<(Pattern, Icon)>,
|
||||
dirs: HashMap<String, Icon>,
|
||||
files: HashMap<String, Icon>,
|
||||
exts: HashMap<String, Icon>,
|
||||
conds: Vec<(Condition, Icon)>,
|
||||
}
|
||||
|
||||
impl Icons {
|
||||
pub fn matches(&self, file: &File) -> Option<&Icon> {
|
||||
if let Some(i) = self.match_by_glob(file) {
|
||||
return Some(i);
|
||||
}
|
||||
|
||||
if let Some(i) = self.match_by_name(file) {
|
||||
return Some(i);
|
||||
}
|
||||
|
||||
let f = |s: &str| match s {
|
||||
"dir" => file.is_dir(),
|
||||
"hidden" => file.is_hidden(),
|
||||
"link" => file.is_link(),
|
||||
"orphan" => file.is_orphan(),
|
||||
"block" => file.is_block(),
|
||||
"char" => file.is_char(),
|
||||
"fifo" => file.is_fifo(),
|
||||
"sock" => file.is_sock(),
|
||||
"exec" => file.is_exec(),
|
||||
"sticky" => file.is_sticky(),
|
||||
_ => false,
|
||||
};
|
||||
self.conds.iter().find(|(c, _)| c.eval(f) == Some(true)).map(|(_, i)| i)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn match_by_glob(&self, file: &File) -> Option<&Icon> {
|
||||
self.globs.iter().find(|(p, _)| p.match_path(&file.url, file.is_dir())).map(|(_, i)| i)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn match_by_name(&self, file: &File) -> Option<&Icon> {
|
||||
let name = file.name()?.to_str()?;
|
||||
if file.is_dir() {
|
||||
self.dirs.get(name).or_else(|| self.dirs.get(&name.to_ascii_lowercase()))
|
||||
} else {
|
||||
self
|
||||
.files
|
||||
.get(name)
|
||||
.or_else(|| self.files.get(&name.to_ascii_lowercase()))
|
||||
.or_else(|| self.exts.get(file.url.extension()?.to_str()?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Icons {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
pub struct Shadow {
|
||||
globs: Vec<ShadowPat>,
|
||||
#[serde(default)]
|
||||
prepend_globs: Vec<ShadowPat>,
|
||||
#[serde(default)]
|
||||
append_globs: Vec<ShadowPat>,
|
||||
|
||||
dirs: Vec<ShadowStr>,
|
||||
#[serde(default)]
|
||||
prepend_dirs: Vec<ShadowStr>,
|
||||
#[serde(default)]
|
||||
append_dirs: Vec<ShadowStr>,
|
||||
|
||||
files: Vec<ShadowStr>,
|
||||
#[serde(default)]
|
||||
prepend_files: Vec<ShadowStr>,
|
||||
#[serde(default)]
|
||||
append_files: Vec<ShadowStr>,
|
||||
|
||||
exts: Vec<ShadowStr>,
|
||||
#[serde(default)]
|
||||
prepend_exts: Vec<ShadowStr>,
|
||||
#[serde(default)]
|
||||
append_exts: Vec<ShadowStr>,
|
||||
|
||||
conds: Vec<ShadowCond>,
|
||||
#[serde(default)]
|
||||
prepend_conds: Vec<ShadowCond>,
|
||||
#[serde(default)]
|
||||
append_conds: Vec<ShadowCond>,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
pub struct ShadowPat {
|
||||
name: Pattern,
|
||||
text: String,
|
||||
fg_dark: Option<Color>,
|
||||
#[allow(dead_code)]
|
||||
fg_light: Option<Color>,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
pub struct ShadowStr {
|
||||
name: String,
|
||||
text: String,
|
||||
fg_dark: Option<Color>,
|
||||
#[allow(dead_code)]
|
||||
fg_light: Option<Color>,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
pub struct ShadowCond {
|
||||
#[serde(rename = "if")]
|
||||
if_: Condition,
|
||||
text: String,
|
||||
fg_dark: Option<Color>,
|
||||
#[allow(dead_code)]
|
||||
fg_light: Option<Color>,
|
||||
}
|
||||
|
||||
let mut shadow = Shadow::deserialize(deserializer)?;
|
||||
Preset::mix(&mut shadow.globs, shadow.prepend_globs, shadow.append_globs);
|
||||
Preset::mix(&mut shadow.dirs, shadow.prepend_dirs, shadow.append_dirs);
|
||||
Preset::mix(&mut shadow.files, shadow.prepend_files, shadow.append_files);
|
||||
Preset::mix(&mut shadow.exts, shadow.prepend_exts, shadow.append_exts);
|
||||
Preset::mix(&mut shadow.conds, shadow.prepend_conds, shadow.append_conds);
|
||||
|
||||
let globs = shadow
|
||||
.globs
|
||||
.into_iter()
|
||||
.map(|v| {
|
||||
(v.name, Icon { text: v.text, style: Style { fg: v.fg_dark, ..Default::default() } })
|
||||
})
|
||||
.collect();
|
||||
|
||||
let conds = shadow
|
||||
.conds
|
||||
.into_iter()
|
||||
.map(|v| {
|
||||
(v.if_, Icon { text: v.text, style: Style { fg: v.fg_dark, ..Default::default() } })
|
||||
})
|
||||
.collect();
|
||||
|
||||
fn as_map(v: Vec<ShadowStr>) -> HashMap<String, Icon> {
|
||||
let mut map = HashMap::with_capacity(v.len());
|
||||
for item in v {
|
||||
map.entry(item.name).or_insert(Icon {
|
||||
text: item.text,
|
||||
style: Style { fg: item.fg_dark, ..Default::default() },
|
||||
});
|
||||
}
|
||||
map.shrink_to_fit();
|
||||
map
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
globs,
|
||||
dirs: as_map(shadow.dirs),
|
||||
files: as_map(shadow.files),
|
||||
exts: as_map(shadow.exts),
|
||||
conds,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -48,13 +48,13 @@ impl Is {
|
|||
pub fn check(&self, cha: &Cha) -> bool {
|
||||
match self {
|
||||
Self::None => true,
|
||||
Self::Block => cha.is_block_device(),
|
||||
Self::Char => cha.is_char_device(),
|
||||
Self::Block => cha.is_block(),
|
||||
Self::Char => cha.is_char(),
|
||||
Self::Exec => cha.is_exec(),
|
||||
Self::Fifo => cha.is_fifo(),
|
||||
Self::Link => cha.is_link(),
|
||||
Self::Orphan => cha.is_orphan(),
|
||||
Self::Sock => cha.is_socket(),
|
||||
Self::Sock => cha.is_sock(),
|
||||
Self::Sticky => cha.is_sticky(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
mod filetype;
|
||||
mod flavor;
|
||||
mod icon;
|
||||
mod icons;
|
||||
mod is;
|
||||
mod theme;
|
||||
|
||||
pub use filetype::*;
|
||||
pub use flavor::*;
|
||||
pub use icon::*;
|
||||
pub use icons::*;
|
||||
pub use is::*;
|
||||
pub use theme::*;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
use std::path::PathBuf;
|
||||
use std::{path::PathBuf, str::FromStr};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
use yazi_shared::{fs::expand_path, theme::Style, Xdg};
|
||||
|
||||
use super::{Filetype, Flavor, Icon};
|
||||
use crate::{validation::check_validation, MERGED_THEME};
|
||||
use super::{Filetype, Flavor, Icons};
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Theme {
|
||||
|
|
@ -23,16 +22,17 @@ pub struct Theme {
|
|||
// File-specific styles
|
||||
#[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)]
|
||||
pub filetypes: Vec<Filetype>,
|
||||
#[serde(rename = "icon", deserialize_with = "Icon::deserialize", skip_serializing)]
|
||||
pub icons: Vec<Icon>,
|
||||
#[serde(rename = "icon", skip_serializing)]
|
||||
pub icons: Icons,
|
||||
}
|
||||
|
||||
impl Default for Theme {
|
||||
fn default() -> Self {
|
||||
let mut theme: Self = toml::from_str(&MERGED_THEME).unwrap();
|
||||
impl FromStr for Theme {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
check_validation(theme.manager.validate());
|
||||
check_validation(theme.which.validate());
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut theme: Self = toml::from_str(s)?;
|
||||
theme.manager.validate()?;
|
||||
theme.which.validate()?;
|
||||
|
||||
if theme.flavor.use_.is_empty() {
|
||||
theme.manager.syntect_theme = expand_path(&theme.manager.syntect_theme);
|
||||
|
|
@ -41,7 +41,7 @@ impl Default for Theme {
|
|||
Xdg::config_dir().join(format!("flavors/{}.yazi/tmtheme.xml", theme.flavor.use_));
|
||||
}
|
||||
|
||||
theme
|
||||
Ok(theme)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
use std::{borrow::Cow, process};
|
||||
|
||||
use validator::{ValidationErrors, ValidationErrorsKind};
|
||||
|
||||
pub fn check_validation(res: Result<(), ValidationErrors>) {
|
||||
let Err(errors) = res else { return };
|
||||
|
||||
for (field, kind) in errors.into_errors() {
|
||||
match kind {
|
||||
ValidationErrorsKind::Struct(errors) => check_validation(Err(*errors)),
|
||||
ValidationErrorsKind::List(errors) => {
|
||||
for (i, errors) in errors {
|
||||
eprint!("Config `{field}[{i}]` format error: ");
|
||||
check_validation(Err(*errors));
|
||||
eprintln!();
|
||||
}
|
||||
}
|
||||
ValidationErrorsKind::Field(error) => {
|
||||
for e in error {
|
||||
eprintln!(
|
||||
"Config `{field}` format error: {}\n",
|
||||
e.message.unwrap_or(Cow::Borrowed("unknown error"))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
process::exit(1);
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
|
||||
use super::SortBy;
|
||||
use crate::MERGED_YAZI;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Validate)]
|
||||
pub struct Which {
|
||||
|
|
@ -10,15 +11,18 @@ pub struct Which {
|
|||
pub sort_by: SortBy,
|
||||
pub sort_sensitive: bool,
|
||||
pub sort_reverse: bool,
|
||||
pub sort_translit: bool,
|
||||
}
|
||||
|
||||
impl Default for Which {
|
||||
fn default() -> Self {
|
||||
impl FromStr for Which {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
which: Which,
|
||||
}
|
||||
|
||||
toml::from_str::<Outer>(&MERGED_YAZI).unwrap().which
|
||||
Ok(toml::from_str::<Outer>(s)?.which)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ homepage = "https://yazi-rs.github.io"
|
|||
repository = "https://github.com/sxyazi/yazi"
|
||||
|
||||
[dependencies]
|
||||
yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.5" }
|
||||
yazi-adapter = { path = "../yazi-adapter", version = "0.2.5" }
|
||||
yazi-boot = { path = "../yazi-boot", version = "0.2.5" }
|
||||
yazi-config = { path = "../yazi-config", version = "0.2.5" }
|
||||
yazi-dds = { path = "../yazi-dds", version = "0.2.5" }
|
||||
|
|
@ -19,27 +19,25 @@ yazi-scheduler = { path = "../yazi-scheduler", version = "0.2.5" }
|
|||
yazi-shared = { path = "../yazi-shared", version = "0.2.5" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = "1.0.82"
|
||||
base64 = "0.22.0"
|
||||
anyhow = "1.0.86"
|
||||
bitflags = "2.5.0"
|
||||
crossterm = "0.27.0"
|
||||
dirs = "5.0.1"
|
||||
futures = "0.3.30"
|
||||
notify = { version = "6.1.1", default-features = false, features = [ "macos_fsevent" ] }
|
||||
parking_lot = "0.12.1"
|
||||
ratatui = "=0.26.1"
|
||||
regex = "1.10.4"
|
||||
parking_lot = "0.12.3"
|
||||
ratatui = "0.27.0"
|
||||
regex = "1.10.5"
|
||||
scopeguard = "1.2.0"
|
||||
serde = "1.0.198"
|
||||
tokio = { version = "1.37.0", features = [ "full" ] }
|
||||
serde = "1.0.203"
|
||||
shell-words = "1.1.0"
|
||||
tokio = { version = "1.38.0", features = [ "full" ] }
|
||||
tokio-stream = "0.1.15"
|
||||
tokio-util = "0.7.10"
|
||||
unicode-width = "0.1.11"
|
||||
tokio-util = "0.7.11"
|
||||
unicode-width = "0.1.13"
|
||||
|
||||
# Logging
|
||||
tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] }
|
||||
|
||||
[target."cfg(unix)".dependencies]
|
||||
libc = "0.2.153"
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
clipboard-win = "5.3.1"
|
||||
libc = "0.2.155"
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ pub struct Opt {
|
|||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self { Self { submit: c.bool("submit") } }
|
||||
}
|
||||
impl From<bool> for Opt {
|
||||
fn from(submit: bool) -> Self { Self { submit } }
|
||||
}
|
||||
|
||||
impl Completion {
|
||||
pub fn close(&mut self, opt: impl Into<Opt>) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{mem, path::{MAIN_SEPARATOR, MAIN_SEPARATOR_STR}};
|
||||
use std::{borrow::Cow, mem, path::{MAIN_SEPARATOR, MAIN_SEPARATOR_STR}};
|
||||
|
||||
use tokio::fs;
|
||||
use yazi_shared::{emit, event::{Cmd, Data}, render, Layer};
|
||||
|
|
@ -33,7 +33,9 @@ impl Completion {
|
|||
}
|
||||
|
||||
self.ticket = opt.ticket;
|
||||
let (parent, child) = Self::split_path(&opt.word);
|
||||
let Some((parent, child)) = Self::split_path(&opt.word) else {
|
||||
return self.close(false);
|
||||
};
|
||||
|
||||
if self.caches.contains_key(&parent) {
|
||||
return self.show(
|
||||
|
|
@ -72,12 +74,24 @@ impl Completion {
|
|||
render!(mem::replace(&mut self.visible, false));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn split_path(s: &str) -> (String, String) {
|
||||
match s.rsplit_once(SEPARATOR) {
|
||||
Some((p, c)) => (format!("{p}{}", MAIN_SEPARATOR), c.to_owned()),
|
||||
None => (".".to_owned(), s.to_owned()),
|
||||
fn split_path(s: &str) -> Option<(String, String)> {
|
||||
if s == "~" {
|
||||
return None; // We don't autocomplete a `~`, but `~/`
|
||||
}
|
||||
|
||||
let s = if let Some(rest) = s.strip_prefix("~") {
|
||||
Cow::Owned(format!(
|
||||
"{}{rest}",
|
||||
dirs::home_dir().unwrap_or_default().to_string_lossy().trim_end_matches(SEPARATOR),
|
||||
))
|
||||
} else {
|
||||
Cow::Borrowed(s)
|
||||
};
|
||||
|
||||
Some(match s.rsplit_once(SEPARATOR) {
|
||||
Some((p, c)) => (format!("{p}{}", MAIN_SEPARATOR), c.to_owned()),
|
||||
None => (".".to_owned(), s.into_owned()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,28 +99,32 @@ impl Completion {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn compare(s: &str, parent: &str, child: &str) -> bool {
|
||||
matches!(Completion::split_path(s), Some((p, c)) if p == parent && c == child)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_split() {
|
||||
assert_eq!(Completion::split_path(""), (".".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path(" "), (".".to_owned(), " ".to_owned()));
|
||||
assert_eq!(Completion::split_path("/"), ("/".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path("//"), ("//".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path("/foo"), ("/".to_owned(), "foo".to_owned()));
|
||||
assert_eq!(Completion::split_path("/foo/"), ("/foo/".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path("/foo/bar"), ("/foo/".to_owned(), "bar".to_owned()));
|
||||
assert!(compare("", ".", ""));
|
||||
assert!(compare(" ", ".", " "));
|
||||
assert!(compare("/", "/", ""));
|
||||
assert!(compare("//", "//", ""));
|
||||
assert!(compare("/foo", "/", "foo"));
|
||||
assert!(compare("/foo/", "/foo/", ""));
|
||||
assert!(compare("/foo/bar", "/foo/", "bar"));
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn test_split() {
|
||||
assert_eq!(Completion::split_path("foo"), (".".to_owned(), "foo".to_owned()));
|
||||
assert_eq!(Completion::split_path("foo\\"), ("foo\\".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path("foo\\bar"), ("foo\\".to_owned(), "bar".to_owned()));
|
||||
assert_eq!(Completion::split_path("foo\\bar\\"), ("foo\\bar\\".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path("C:\\"), ("C:\\".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path("C:\\foo"), ("C:\\".to_owned(), "foo".to_owned()));
|
||||
assert_eq!(Completion::split_path("C:\\foo\\"), ("C:\\foo\\".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path("C:\\foo\\bar"), ("C:\\foo\\".to_owned(), "bar".to_owned()));
|
||||
assert!(compare("foo", ".", "foo"));
|
||||
assert!(compare("foo\\", "foo\\", ""));
|
||||
assert!(compare("foo\\bar", "foo\\", "bar"));
|
||||
assert!(compare("foo\\bar\\", "foo\\bar\\", ""));
|
||||
assert!(compare("C:\\", "C:\\", ""));
|
||||
assert!(compare("C:\\foo", "C:\\", "foo"));
|
||||
assert!(compare("C:\\foo\\", "C:\\foo\\", ""));
|
||||
assert!(compare("C:\\foo\\bar", "C:\\foo\\", "bar"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::{collections::{HashMap, HashSet}, fs::Metadata, mem, ops::Deref, sync::
|
|||
|
||||
use tokio::{fs::{self, DirEntry}, select, sync::mpsc::{self, UnboundedReceiver}};
|
||||
use yazi_config::{manager::SortBy, MANAGER};
|
||||
use yazi_shared::fs::{accessible, File, FilesOp, Url, FILES_TICKET};
|
||||
use yazi_shared::fs::{maybe_exists, File, FilesOp, Url, FILES_TICKET};
|
||||
|
||||
use super::{FilesSorter, Filter};
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ impl Files {
|
|||
Ok(m) if mtime == m.modified().ok() => {}
|
||||
Ok(m) => return Some(m),
|
||||
Err(e) => {
|
||||
if accessible(url).await {
|
||||
if maybe_exists(url).await {
|
||||
FilesOp::IOErr(url.clone(), e.kind()).emit();
|
||||
} else if let Some(p) = url.parent_url() {
|
||||
FilesOp::Deleting(p, vec![url.clone()]).emit();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{ffi::OsStr, ops::Range};
|
||||
use std::{ffi::OsStr, fmt::Display, ops::Range};
|
||||
|
||||
use anyhow::Result;
|
||||
use regex::bytes::{Regex, RegexBuilder};
|
||||
|
|
@ -9,10 +9,6 @@ pub struct Filter {
|
|||
regex: Regex,
|
||||
}
|
||||
|
||||
impl PartialEq for Filter {
|
||||
fn eq(&self, other: &Self) -> bool { self.raw == other.raw }
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
pub fn new(s: &str, case: FilterCase) -> Result<Self> {
|
||||
let regex = match case {
|
||||
|
|
@ -35,6 +31,14 @@ impl Filter {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Filter {
|
||||
fn eq(&self, other: &Self) -> bool { self.raw == other.raw }
|
||||
}
|
||||
|
||||
impl Display for Filter {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&self.raw) }
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq)]
|
||||
pub enum FilterCase {
|
||||
Smart,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{cmp::Ordering, collections::HashMap, mem};
|
||||
|
||||
use yazi_config::manager::SortBy;
|
||||
use yazi_shared::{fs::{File, Url}, natsort};
|
||||
use yazi_shared::{fs::{File, Url}, natsort, Transliterator};
|
||||
|
||||
#[derive(Clone, Copy, Default, PartialEq)]
|
||||
pub struct FilesSorter {
|
||||
|
|
@ -9,6 +9,7 @@ pub struct FilesSorter {
|
|||
pub sensitive: bool,
|
||||
pub reverse: bool,
|
||||
pub dir_first: bool,
|
||||
pub translit: bool,
|
||||
}
|
||||
|
||||
impl FilesSorter {
|
||||
|
|
@ -76,7 +77,16 @@ impl FilesSorter {
|
|||
return promote;
|
||||
}
|
||||
|
||||
let ordering = natsort(entities[a], entities[b], !self.sensitive);
|
||||
let ordering = if !self.translit {
|
||||
natsort(entities[a], entities[b], !self.sensitive)
|
||||
} else {
|
||||
natsort(
|
||||
entities[a].transliterate().as_bytes(),
|
||||
entities[b].transliterate().as_bytes(),
|
||||
!self.sensitive,
|
||||
)
|
||||
};
|
||||
|
||||
if self.reverse { ordering.reverse() } else { ordering }
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use crossterm::event::KeyCode;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use yazi_adapter::Dimension;
|
||||
use yazi_config::{keymap::{Control, Key}, KEYMAP};
|
||||
use yazi_shared::{render, render_and, term::Term, Layer};
|
||||
use yazi_shared::{render, render_and, Layer};
|
||||
|
||||
use super::HELP_MARGIN;
|
||||
use crate::input::Input;
|
||||
|
|
@ -22,7 +23,7 @@ pub struct Help {
|
|||
|
||||
impl Help {
|
||||
#[inline]
|
||||
pub fn limit() -> usize { Term::size().rows.saturating_sub(HELP_MARGIN) as usize }
|
||||
pub fn limit() -> usize { Dimension::available().rows.saturating_sub(HELP_MARGIN) as usize }
|
||||
|
||||
pub fn toggle(&mut self, layer: Layer) {
|
||||
self.visible = !self.visible;
|
||||
|
|
@ -43,15 +44,15 @@ impl Help {
|
|||
};
|
||||
|
||||
match key {
|
||||
Key { code: KeyCode::Esc, shift: false, ctrl: false, alt: false } => {
|
||||
Key { code: KeyCode::Esc, shift: false, ctrl: false, alt: false, super_: false } => {
|
||||
self.in_filter = None;
|
||||
render!();
|
||||
}
|
||||
Key { code: KeyCode::Enter, shift: false, ctrl: false, alt: false } => {
|
||||
Key { code: KeyCode::Enter, shift: false, ctrl: false, alt: false, super_: false } => {
|
||||
self.in_filter = None;
|
||||
return render_and!(true); // Don't do the `filter_apply` below, since we already have the filtered results.
|
||||
}
|
||||
Key { code: KeyCode::Backspace, shift: false, ctrl: false, alt: false } => {
|
||||
Key { code: KeyCode::Backspace, shift: false, ctrl: false, alt: false, super_: false } => {
|
||||
input.backspace(false);
|
||||
}
|
||||
_ => {
|
||||
|
|
@ -106,7 +107,7 @@ impl Help {
|
|||
return None;
|
||||
}
|
||||
if let Some(kw) = self.keyword() {
|
||||
return Some((kw.width() as u16, Term::size().rows));
|
||||
return Some((kw.width() as u16, Dimension::available().rows));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ impl Input {
|
|||
let snap = self.snap_mut();
|
||||
|
||||
match opt.kind.as_str() {
|
||||
"all" => self.kill_range(..),
|
||||
"bol" => {
|
||||
let end = snap.idx(snap.cursor).unwrap_or(snap.len());
|
||||
self.kill_range(..end)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use yazi_plugin::CLIPBOARD;
|
||||
use yazi_shared::{event::Cmd, render};
|
||||
|
||||
use crate::{input::{op::InputOp, Input}, CLIPBOARD};
|
||||
use crate::input::{op::InputOp, Input};
|
||||
|
||||
pub struct Opt {
|
||||
before: bool,
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ use std::ops::Range;
|
|||
use tokio::sync::mpsc::UnboundedSender;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use yazi_config::{popup::Position, INPUT};
|
||||
use yazi_plugin::CLIPBOARD;
|
||||
use yazi_shared::{render, InputError};
|
||||
|
||||
use super::{mode::InputMode, op::InputOp, InputSnap, InputSnaps};
|
||||
use crate::CLIPBOARD;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Input {
|
||||
|
|
@ -87,16 +87,17 @@ impl Input {
|
|||
}
|
||||
|
||||
pub(super) fn flush_value(&mut self) {
|
||||
let Some(tx) = &self.callback else { return };
|
||||
self.ticket = self.ticket.wrapping_add(1);
|
||||
|
||||
if self.realtime {
|
||||
let value = self.snap().value.clone();
|
||||
self.callback.as_ref().unwrap().send(Err(InputError::Typed(value))).ok();
|
||||
tx.send(Err(InputError::Typed(value))).ok();
|
||||
}
|
||||
|
||||
if self.completion {
|
||||
let before = self.partition()[0].to_owned();
|
||||
self.callback.as_ref().unwrap().send(Err(InputError::Completed(before, self.ticket))).ok();
|
||||
tx.send(Err(InputError::Completed(before, self.ticket))).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ impl InputSnaps {
|
|||
}
|
||||
|
||||
pub(super) fn tag(&mut self, limit: usize) -> bool {
|
||||
if self.versions.len() <= self.idx {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sync *current* cursor position to the *last* version:
|
||||
// Save offset/cursor/ect. of the *current* as the last version,
|
||||
// while keeping the *last* value unchanged.
|
||||
|
|
@ -49,7 +53,7 @@ impl InputSnaps {
|
|||
}
|
||||
|
||||
pub(super) fn redo(&mut self) -> bool {
|
||||
if self.idx + 1 == self.versions.len() {
|
||||
if self.idx + 1 >= self.versions.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
clippy::unit_arg
|
||||
)]
|
||||
|
||||
mod clipboard;
|
||||
pub mod completion;
|
||||
pub mod folder;
|
||||
pub mod help;
|
||||
|
|
@ -19,12 +18,9 @@ pub mod tab;
|
|||
pub mod tasks;
|
||||
pub mod which;
|
||||
|
||||
pub use clipboard::*;
|
||||
pub use step::*;
|
||||
|
||||
pub fn init() {
|
||||
CLIPBOARD.with(Default::default);
|
||||
|
||||
manager::WATCHED.with(Default::default);
|
||||
manager::LINKED.with(Default::default);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ use anyhow::{anyhow, Result};
|
|||
use scopeguard::defer;
|
||||
use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}};
|
||||
use yazi_config::{OPEN, PREVIEW};
|
||||
use yazi_dds::Pubsub;
|
||||
use yazi_proxy::{AppProxy, TasksProxy, HIDER, WATCHER};
|
||||
use yazi_shared::{fs::{accessible, max_common_root, File, FilesOp, Url}, term::Term};
|
||||
use yazi_shared::{fs::{max_common_root, maybe_exists, paths_to_same_file, File, FilesOp, Url}, terminal_clear};
|
||||
|
||||
use crate::manager::Manager;
|
||||
|
||||
|
|
@ -51,7 +52,7 @@ impl Manager {
|
|||
old: Vec<PathBuf>,
|
||||
new: Vec<PathBuf>,
|
||||
) -> Result<()> {
|
||||
Term::clear(&mut stderr())?;
|
||||
terminal_clear(&mut stderr())?;
|
||||
if old.len() != new.len() {
|
||||
eprintln!("Number of old and new differ, press ENTER to exit");
|
||||
stdin().read_exact(&mut [0]).await?;
|
||||
|
|
@ -83,7 +84,7 @@ impl Manager {
|
|||
for (o, n) in todo {
|
||||
let (old, new) = (root.join(&o), root.join(&n));
|
||||
|
||||
if accessible(&new).await {
|
||||
if maybe_exists(&new).await && !paths_to_same_file(&old, &new).await {
|
||||
failed.push((o, n, anyhow!("Destination already exists")));
|
||||
} else if let Err(e) = fs::rename(&old, &new).await {
|
||||
failed.push((o, n, e.into()));
|
||||
|
|
@ -95,6 +96,7 @@ impl Manager {
|
|||
}
|
||||
|
||||
if !succeeded.is_empty() {
|
||||
Pubsub::pub_from_bulk(succeeded.iter().map(|(u, f)| (u, &f.url)).collect());
|
||||
FilesOp::Upserting(cwd, succeeded).emit();
|
||||
}
|
||||
drop(permit);
|
||||
|
|
@ -106,7 +108,7 @@ impl Manager {
|
|||
}
|
||||
|
||||
async fn output_failed(failed: Vec<(PathBuf, PathBuf, anyhow::Error)>) -> Result<()> {
|
||||
Term::clear(&mut stderr())?;
|
||||
terminal_clear(&mut stderr())?;
|
||||
|
||||
{
|
||||
let mut stderr = BufWriter::new(stderr().lock());
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use std::path::PathBuf;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use tokio::fs;
|
||||
use yazi_config::popup::InputCfg;
|
||||
use yazi_proxy::{InputProxy, ManagerProxy};
|
||||
use yazi_shared::{event::Cmd, fs::{accessible, File, FilesOp, Url}};
|
||||
use yazi_proxy::{InputProxy, TabProxy, WATCHER};
|
||||
use yazi_shared::{event::Cmd, fs::{maybe_exists, ok_or_not_found, symlink_realpath, File, FilesOp, Url}};
|
||||
|
||||
use crate::manager::Manager;
|
||||
|
||||
|
|
@ -24,29 +25,42 @@ impl Manager {
|
|||
let Some(Ok(name)) = result.recv().await else {
|
||||
return Ok(());
|
||||
};
|
||||
if name.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let path = cwd.join(&name);
|
||||
if !opt.force && accessible(&path).await {
|
||||
let new = cwd.join(&name);
|
||||
if !opt.force && maybe_exists(&new).await {
|
||||
match InputProxy::show(InputCfg::overwrite()).recv().await {
|
||||
Some(Ok(c)) if c == "y" || c == "Y" => (),
|
||||
_ => return Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
if name.ends_with('/') || name.ends_with('\\') {
|
||||
fs::create_dir_all(&path).await?;
|
||||
} else {
|
||||
fs::create_dir_all(&path.parent().unwrap()).await.ok();
|
||||
fs::File::create(&path).await?;
|
||||
}
|
||||
|
||||
let child =
|
||||
Url::from(path.components().take(cwd.components().count() + 1).collect::<PathBuf>());
|
||||
if let Ok(f) = File::from(child.clone()).await {
|
||||
FilesOp::Creating(cwd, vec![f]).emit();
|
||||
ManagerProxy::hover(Some(child));
|
||||
}
|
||||
Ok::<(), anyhow::Error>(())
|
||||
Self::create_do(new, name.ends_with('/') || name.ends_with('\\')).await
|
||||
});
|
||||
}
|
||||
|
||||
async fn create_do(new: Url, dir: bool) -> Result<()> {
|
||||
let Some(parent) = new.parent_url() else { return Ok(()) };
|
||||
let _permit = WATCHER.acquire().await.unwrap();
|
||||
|
||||
if dir {
|
||||
fs::create_dir_all(&new).await?;
|
||||
} else if let Ok(real) = symlink_realpath(&new).await {
|
||||
ok_or_not_found(fs::remove_file(&new).await)?;
|
||||
FilesOp::Deleting(parent.clone(), vec![Url::from(real)]).emit();
|
||||
fs::File::create(&new).await?;
|
||||
} else {
|
||||
fs::create_dir_all(&parent).await.ok();
|
||||
ok_or_not_found(fs::remove_file(&new).await)?;
|
||||
fs::File::create(&new).await?;
|
||||
}
|
||||
|
||||
if let Ok(f) = File::from(new.clone()).await {
|
||||
FilesOp::Upserting(parent, HashMap::from_iter([(f.url(), f)])).emit();
|
||||
TabProxy::reveal(&new)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue