From 90d846ea203e1eae5f2c0ceed3151faad6fd50d2 Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Sat, 26 Apr 2025 23:21:08 +0300 Subject: [PATCH] Update 2025-04-26 --- config/yazi/package.toml | 44 +- .../plugins/augment-command.yazi/README.md | 439 ++++--- .../plugins/augment-command.yazi/main.lua | 1043 ++++++++++++----- config/yazi/plugins/glow.yazi/main.lua | 6 +- config/yazi/plugins/hexyl.yazi/main.lua | 6 +- config/yazi/plugins/nbpreview.yazi/README.md | 14 +- config/yazi/plugins/nbpreview.yazi/main.lua | 9 +- config/yazi/plugins/ouch.yazi/README.md | 3 +- config/yazi/plugins/ouch.yazi/main.lua | 2 +- .../plugins/relative-motions.yazi/README.md | 2 +- .../plugins/relative-motions.yazi/main.lua | 68 +- config/yazi/plugins/yatline.yazi/main.lua | 10 +- config/zsh/.zshrc | 2 + 13 files changed, 1137 insertions(+), 511 deletions(-) diff --git a/config/yazi/package.toml b/config/yazi/package.toml index 0844ae9a..cc58e779 100644 --- a/config/yazi/package.toml +++ b/config/yazi/package.toml @@ -1,17 +1,17 @@ [[plugin.deps]] use = "AnirudhG07/nbpreview" -rev = "f8879b3" -hash = "d378328e5d0a1b9fb9f04ab3aade4575" +rev = "bc573d5" +hash = "ab61298c387a63e781a87129f533883b" [[plugin.deps]] use = "Reledia/glow" -rev = "c76bf4f" -hash = "a6b78bf9af5390e3a85a6951fbb7b93" +rev = "2da96e3" +hash = "f9ee1436e3b853508d87f7d49dce56e6" [[plugin.deps]] use = "Reledia/hexyl" -rev = "228a9ef" -hash = "cdc65cfe4e60e1bf5afe5769d074fa9c" +rev = "016a09b" +hash = "50da29476e744dba37d77fb209328fd1" [[plugin.deps]] use = "Sonico98/exifaudio" @@ -20,18 +20,18 @@ hash = "666ccba55119fba4c25b8ad354b2855c" [[plugin.deps]] use = "dedukun/relative-motions" -rev = "61ae795" -hash = "65d576a76597aac2a428dc7ec0eda2ce" +rev = "ce2e890" +hash = "23915860e59348bf4166778bb0e606f7" [[plugin.deps]] use = "hankertrix/augment-command" -rev = "269e3cf" -hash = "f8c8ddd15b2227340fa7f585df71580" +rev = "91ba6c5" +hash = "6ac13898ec80c623e2d9ea90b947c86a" [[plugin.deps]] use = "imsi32/yatline" -rev = "6b0fc1e" -hash = "ab115c6cc77f5710c27f39dfa2f3d4d" +rev = "2ecf715" +hash = "38e2ea4703ea606d4eef574e8e8b8fd7" [[plugin.deps]] use = "kirasok/torrent-preview" @@ -40,8 +40,8 @@ hash = "6af40ce6b2cd849b5fa32de04a598b06" [[plugin.deps]] use = "ndtoan96/ouch" -rev = "558188d" -hash = "2d0afef7b50747c543c4304004a72cec" +rev = "2496cd9" +hash = "5b9dea47776a30946cfbf83232d18fb1" [[plugin.deps]] use = "pirafrank/what-size" @@ -50,28 +50,18 @@ hash = "6e789212eb41d937bab04877ca361099" [[plugin.deps]] use = "yazi-rs/plugins:git" -rev = "9a09505" +rev = "4b027c7" hash = "e0ada736ea676c2bbb3ec705a49526ef" -[[plugin.deps]] -use = "yazi-rs/plugins:max-preview" -rev = "9a09505" -hash = "a8025f2bb311e869069364fba01abffc" - [[plugin.deps]] use = "yazi-rs/plugins:chmod" -rev = "9a09505" +rev = "4b027c7" hash = "2f1053f89d1a301a648ab181d0948e38" [[plugin.deps]] use = "yazi-rs/plugins:full-border" -rev = "9a09505" +rev = "4b027c7" hash = "1f3dad061209081a6b04dd6ff2cb06c7" -[[plugin.deps]] -use = "yazi-rs/plugins:hide-preview" -rev = "9a09505" -hash = "53f826884e2c7e521cecc492a7f31d7e" - [flavor] deps = [] diff --git a/config/yazi/plugins/augment-command.yazi/README.md b/config/yazi/plugins/augment-command.yazi/README.md index a19c3008..3239382f 100644 --- a/config/yazi/plugins/augment-command.yazi/README.md +++ b/config/yazi/plugins/augment-command.yazi/README.md @@ -31,6 +31,7 @@ plugin. - [Arrow (`arrow`)](#arrow-arrow) - [New commands](#new-commands) - [Parent arrow (`parent_arrow`)](#parent-arrow-parent_arrow) + - [Archive (`archive`)](#archive-archive) - [Editor (`editor`)](#editor-editor) - [Pager (`pager`)](#pager-pager) - [Usage](#usage) @@ -40,7 +41,7 @@ plugin. ## Requirements -- [Yazi][yazi-link] v25.3.2+ +- [Yazi][yazi-link] v25.4.8+ - [`7z` or `7zz` command][7z-link] - [`file` command][file-command-link] @@ -79,12 +80,16 @@ ya pack -u | `extract_retries` | An integer, like `1`, `3`, `10`, etc. | `3` | This option determines how many times the plugin will retry opening an encrypted or password-protected archive when a wrong password is given. This value plus 1 is the total number of times the plugin will try opening an encrypted or password-protected archive. | | `recursively_extract_archives` | `true` or `false` | `true` | This option determines whether the plugin will extract all archives inside an archive file recursively. If this option is set to `false`, archive files inside an archive will not be extracted, and you will have to manually extract them yourself. | | `preserve_file_permissions` | `true` or `false` | `false` | This option determines whether to preserve the file permissions of the items in the extracted archive. Setting this option to `true` will preserve the file permissions of the extracted items. It requires the [`tar` command][gnu-tar-link] and will only work on `tar` archives, or tarballs, as [`7z`][7z-link] does not support preserving file permissions. You will receive a warning if you have this option set but [`tar`][gnu-tar-link] is not installed. Do note that there are significant security implications of setting this option to `true`, as any executable file or binary in an archive can be immediately executed after it is extracted, which can compromise your system if you extract a malicious archive. As such, the default value is `false`, and it is strongly recommended to leave it as such. | +| `encrypt_archives` | `true` or `false` | `false` | This option determines whether the plugin will encrypt the archives it creates. If this option is set to `true`, the plugin will prompt for the archive password when creating an archive to encrypt it with. The plugin will prompt twice for the password, and will check both of them to see if they match. If they do, the password entered is set as the archive password. Otherwise, the plugin will show an error stating the passwords do not match, and prompt for two passwords again. Cancelling either of the prompts will cancel the whole process. | +| `encrypt_archive_headers` | `true` or `false` | `false` | This option determines whether the plugin will encrypt the headers of the archives it creates. If this option is set to `true`, the plugin will encrypt the headers of all `7z` archives, which means the file list cannot be previewed and Yazi will not be able to preview the contents of the archive. This encryption is only available to `7z` archives, so the plugin will show a warning message when this option is used, but the selected archive file type, does not support header encryption, like a `zip` archive, but will continue with the creation of the encrypted archive. This option has no effect when the archive is not encrypted, which is when `encrypt_archives` is set to `false`. | +| `reveal_created_archive` | `true` or `false` | `true` | This option determines whether the plugin will automatically hover over the created archive once created. | +| `remove_archived_files` | `true` or `false` | `false` | This option determines whether the plugin will automatically remove the files that were added to the created archive. | | `must_have_hovered_item` | `true` or `false` | `true` | This option stops the plugin from executing any commands when there is no hovered item. | | `skip_single_subdirectory_on_enter` | `true` or `false` | `true` | Skip directories when there is only one subdirectory and no other files when entering directories. This behaviour can be turned off by passing the `--no-skip` flag to the `enter` or `open` commands. | | `skip_single_subdirectory_on_leave` | `true` or `false` | `true` | Skip directories when there is only one subdirectory and no other files when leaving directories. This behaviour can be turned off by passing the `--no-skip` flag to the `leave` command. | | `smooth_scrolling` | `true` or `false` | `false` | Self-explanatory, this option enables smooth scrolling. | | `scroll_delay` | A floating point number, like `0.02`, `0.05`, `0.1`, etc. | `0.02` | The delay, in seconds, between each call of the `arrow` command to scroll through the file list. The smaller the `scroll_delay`, the faster the file list is scrolled. Avoid setting a `scroll_delay` that is more than `1` second. This is due to the plugin being asynchronous, which will result in the plugin continuing to call the `arrow` command even when the directory has changed, or when you are in a different application that doesn't block Yazi, resulting in unexpected behaviour. | -| `wraparound_file_navigation` | `true` or `false` | `false` | Wrap around from the bottom to the top or from the top to the bottom when using the `arrow` or `parent_arrow` command to navigate. | +| `wraparound_file_navigation` | `true` or `false` | `true` | Wrap around from the bottom to the top or from the top to the bottom when using the `arrow` or `parent_arrow` command to navigate. | If you would like to use the default configuration, which is shown below, you don't need to add anything to your `~/.config/yazi/init.lua` @@ -111,12 +116,16 @@ require("augment-command"):setup({ extract_retries = 3, recursively_extract_archives = true, preserve_file_permissions = false, + encrypt_archives = false, + encrypt_archive_headers = false, + reveal_created_archive = true, + remove_archived_files = false, must_have_hovered_item = true, skip_single_subdirectory_on_enter = true, skip_single_subdirectory_on_leave = true, smooth_scrolling = false, scroll_delay = 0.02, - wraparound_file_navigation = false, + wraparound_file_navigation = true, }) ``` @@ -139,8 +148,9 @@ require("augment-command"):setup({ open_file_after_creation = true, enter_directory_after_creation = true, extract_retries = 5, + encrypt_archives = true, smooth_scrolling = true, - wraparound_file_navigation = true, + wraparound_file_navigation = false, }) ``` @@ -148,7 +158,8 @@ require("augment-command"):setup({ All commands that can operate on multiple files and directories, like `open`, `rename`, `remove` and `shell`, -as well as the new commands `extract`, `editor` and `pager`, +as well as the new commands `extract`, `archive`, +`editor` and `pager`, now determine an item group to operate on. By default, the command will operate on the hovered item, unless the hovered item is also selected, @@ -182,9 +193,11 @@ then it will operate on the selected items. - When `smart_enter` is set to `true`, it calls the `enter` command when the hovered item is a directory. -- `--smart` flag to use one command to `open` files and `enter` directories. - This flag will cause the `open` command to call the `enter` command when - the hovered item is a directory even when `smart_enter` is set to `false`. +- `--smart` flag to use one command to `open` files + and `enter` directories. + This flag will cause the `open` command to call + the `enter` command when the hovered item is a directory + even when `smart_enter` is set to `false`. This allows you to set a key to use this behaviour with the `open` command instead of using it for every `open` command. @@ -221,16 +234,18 @@ then it will operate on the selected items. ### Extract (`extract`) -- Technically this is a new command, as Yazi does not provide an `extract` - command. However, Yazi does provide a built-in plugin called `extract`, +- Technically this is a new command, + as Yazi does not provide an `extract` command. + However, Yazi does provide a built-in plugin called `extract`, so this command is included in the [augmented commands section](#augmented-commands) instead of the [new commands section](#new-commands). - This command requires the [`7z` or `7zz` command][7z-link] to be present to extract the archives, as well as the [`file` command][file-command-link] to check if a file is an archive or not. -- You are not meant to use this command directly. However, you can do so - if you like, as the extract command is also augmented as stated in +- You are not meant to use this command directly. + However, you can do so if you like, + as the extract command is also augmented as stated in [this section above][augment-section]. Videos: @@ -251,21 +266,24 @@ then it will operate on the selected items. [extract-behaviour-video] -- Instead, this command is intended to replace the built-in `extract` plugin, - which is used for the `extract` opener. This way, you can use the +- Instead, this command is intended to replace the + built-in `extract` plugin, which is used for the `extract` opener. + This way, you can use the features that come with the augmented `extract` command, like recursively extracting archives, with the `open` command. - This is the intended way to use this command, as the `open` command is - meant to be the command that opens everything, so it is a bit - counterintuitive to have to use a separate key to extract archives. + This is the intended way to use this command, + as the `open` command is meant to be the command + that opens everything, so it is a bit counterintuitive + to have to use a separate key to extract archives. To replace the built-in `extract` plugin, copy the [`extract` openers section][yazi-yazi-toml-extract-openers] in [Yazi's default `yazi.toml`][yazi-yazi-toml] into your `yazi.toml`, - which is located at `~/.config/yazi/yazi.toml` for Linux and macOS, and - `%AppData%\yazi\config\yazi.toml` file on Windows. - Make sure that the `extract` openers are under the `opener` key in your - `yazi.toml`. Then replace `extract` with `augmented-extract`, + which is located at `~/.config/yazi/yazi.toml` for Linux and macOS, + and `%AppData%\yazi\config\yazi.toml` file on Windows. + Make sure that the `extract` openers are + under the `opener` key in your `yazi.toml`. + Then replace `extract` with `augmented-extract`, and you will be using the plugin's `extract` command instead of Yazi's built-in `extract` plugin. @@ -282,7 +300,8 @@ then it will operate on the selected items. ] ``` - If that exceeds your editor's line length limit, another way to do it is: + If that exceeds your editor's line length limit, + another way to do it is: ```toml # ~/.config/yazi/yazi.toml for Linux and macOS @@ -331,7 +350,7 @@ then it will operate on the selected items. Video: - [extract-encrypted-archive] + [extract-encrypted-archive-video] - The `preserve_file_permissions` configuration option applies to the `extract` command, and requires the [`tar` command][gnu-tar-link] @@ -353,24 +372,43 @@ then it will operate on the selected items. if it finds the [`gtar` command][gnu-tar-link] instead of the [Apple provided `tar` command][apple-tar-link]. - Setting the `preserve_file_permissions` configuration option to `true` - will preserve the file permissions of the files contained in a `tar` - archive or tarball. + Setting the `preserve_file_permissions` configuration + option to `true` will preserve the file permissions + of the files contained in a `tar` archive or tarball. - This has considerable security implications, as executables extracted from - all `tar` archives can be immediately executed on your system, possibly - compromising your system if you extract a malicious `tar` archive. - Hence, this option is set to `false` by default, and should be left as such. - This option is provided for your convenience, but do seriously consider - if such convenience is worth the risk of extracting a malicious `tar` - archive that executes malware on your system. + This has considerable security implications, + as executables extracted from + all `tar` archives can be immediately executed on your system, + possibly compromising your system if you extract a + malicious `tar` archive. + Hence, this option is set to `false` by default, + and should be left as such. + This option is provided for your convenience, + but do seriously consider if such convenience + is worth the risk of extracting a malicious `tar` archive + that executes malware on your system. + +- `--reveal` flag to automatically hover the files + that have been extracted. + + Video: + + [extract-reveal-extracted-item-video] + +- `--remove` flag to automatically remove the archive + after the files have been extracted. + + Video: + + [extract-remove-extracted-archive-video] ### Enter (`enter`) - When `smart_enter` is set to `true`, it calls the `open` command when the hovered item is a file. -- `--smart` flag to use one command to `enter` directories and `open` files. - This flag will cause the `enter` command to call the `open` command when +- `--smart` flag to use one command to `enter` + directories and `open` files. This flag will cause + the `enter` command to call the `open` command when the selected items or the hovered item is a file, even when `smart_enter` is set to `false`. This allows you to set a key to use this behaviour @@ -462,30 +500,36 @@ then it will operate on the selected items. ### Create (`create`) -- You should use Yazi's default `create` command instead of this augmented - `create` command if you don't want the paths without file extensions to - be created as directories by default, and you don't care about automatically +- You should use Yazi's default `create` command instead + of this augmented `create` command if you + don't want the paths without file extensions to be created + as directories by default, and you don't care about automatically opening and entering the created file and directory respectively. -- The `create` command has a different behaviour from Yazi's `create` command. +- The `create` command has a different behaviour from + Yazi's `create` command. When the path given to the command doesn't have a file extension, the `create` command will create a directory instead of a file, unlike Yazi's `create` command. Other that this major difference, - the `create` command functions identically to Yazi's `create` command, + the `create` command functions identically + to Yazi's `create` command, which means that you can use a trailing `/` on Linux and macOS, or `\` on Windows to create a directory. It will also recursively create directories to ensure that the path given exists. - It also supports all the options supported by Yazi's `create` command, + It also supports all the options supported + by Yazi's `create` command, so you can pass them to the command and expect the same behaviour. - The rationale for this behaviour is that creating a path without - a file extension usually means you intend to create a directory instead - of a file, as files usually have file extensions. + a file extension usually means you intend to + create a directory instead of a file, + as files usually have file extensions. Video: [create-behaviour-video] -- When `open_file_after_creation` is set to `true`, the `create` command - will `open` the created file. This behaviour can also be enabled by +- When `open_file_after_creation` is set to `true`, + the `create` command will `open` the created file. + This behaviour can also be enabled by passing the `--open` flag to the `create` command. Video: @@ -501,16 +545,17 @@ then it will operate on the selected items. [create-and-enter-directories-video] - To enable both behaviours with flags, just pass both the `--open` flag - and the `--enter` flag to the `create` command. + To enable both behaviours with flags, just pass both the + `--open` flag and the `--enter` flag to the `create` command. Video: [create-and-open-files-and-directories-video] - If you would like to use the behaviour of Yazi's `create` command, - probably because you would like to automatically open and enter the created - file and directory respectively, you can either set + probably because you would like to automatically open + and enter the created file and directory respectively, + you can either set `use_default_create_behaviour` to `true`, or pass the `--default-behaviour` flag to the `create` command. @@ -545,9 +590,9 @@ then it will operate on the selected items. [shell-behaviour-video] - To use this command, the syntax is exactly the same as the default - `shell` command provided by Yazi. You just provide the command you want and - provide any Yazi shell variable, which is documented - [here][yazi-shell-variables]. + `shell` command provided by Yazi. You just provide + the command you want and provide any Yazi shell variable, + which is documented [here][yazi-shell-variables]. The plugin will automatically replace the shell variable you give with the file paths for the item group before executing the command. @@ -577,8 +622,10 @@ then it will operate on the selected items. desc = "Open the pager" ``` - It is also used in the `editor` command, since you usually wouldn't use - your text editor to open directories, especially if you are already using + It is also used in the `editor` command, + since you usually wouldn't use + your text editor to open directories, + especially if you are already using a terminal file manager like [Yazi][yazi-link]. The `editor` command is essentially: @@ -598,12 +645,13 @@ then it will operate on the selected items. #### Passing arguments to the `shell` command -Ideally, you will want to avoid using backslashes to escape the shell command -arguments, so here are a few ways to do it: +Ideally, you will want to avoid using backslashes to escape +the shell command arguments, so here are a few ways to do it: 1. Shell arguments that don't have special shell variables - on Linux and macOS, like `$SHELL`, or don't have special shell characters - like `>`, `|` or spaces, need not be quoted with double quotes `"` + on Linux and macOS, like `$SHELL`, or don't have + special shell characters like `>`, `|` or spaces, + need not be quoted with double quotes `"` or single quotes `'` respectively. For example: @@ -616,15 +664,18 @@ arguments, so here are a few ways to do it: desc = "Open with bat" ``` - Even though the `$@` argument above is considered a shell variable in Linux - and macOS, the plugin automatically replaces it with the full path - of the items in the item group, so it does not need to be quoted with + Even though the `$@` argument above is considered + a shell variable in Linux and macOS, + the plugin automatically replaces it with the full path + of the items in the item group, + so it does not need to be quoted with double quotes `"`, as it is expanded by the plugin, and not meant to be expanded by the shell. -2. If the arguments to the `shell` command have special shell variables - on Linux and macOS, like `$SHELL`, or special shell characters like - `>`, `|`, or spaces, use `--` to denote the end of the flags and options +2. If the arguments to the `shell` command have special + shell variables on Linux and macOS, like `$SHELL`, + or special shell characters like `>`, `|`, or spaces, + use `--` to denote the end of the flags and options passed to the `shell` command. For example: @@ -646,10 +697,11 @@ arguments, so here are a few ways to do it: desc = "Open a shell and say hello inside the opened shell" ``` -3. If the arguments passed to the `shell` command themselves contain arguments - that have special shell variables on Linux and macOS, like `$SHELL`, - or special shell characters like `>`, `|`, or spaces, - use the triple single quote `'''` delimiter for the `run` string. +3. If the arguments passed to the `shell` command themselves + contain arguments that have special shell variables on + Linux and macOS, like `$SHELL`, or special shell characters + like `>`, `|`, or spaces, use the triple single quote + `'''` delimiter for the `run` string. ```toml # ~/.config/yazi/keymap.toml on Linux and macOS @@ -687,13 +739,16 @@ arguments, so here are a few ways to do it: desc = "Email files using Mozilla Thunderbird" ``` - Once again, the `$@` variable above does not need to be quoted in double - quotes `"` as it is expanded by the plugin instead of the shell. + Once again, the `$@` variable above does not need to be quoted + in double quotes `"` as it is expanded by the plugin + instead of the shell. -If the above few methods to avoid using backslashes within your shell command -to escape the quotes are still insufficient for your use case, -it is probably more appropriate to write a shell script in a separate file -and execute that instead of writing the shell command inline +If the above few methods to avoid using backslashes +within your shell command to escape the quotes are +still insufficient for your use case, +it is probably more appropriate to write a shell script +in a separate file and execute that instead of +writing the shell command inline in your `keymap.toml` file. ### Paste (`paste`) @@ -709,7 +764,8 @@ in your `keymap.toml` file. - `--smart` flag to enable pasting in the hovered directory without entering the directory. This flag will cause the `paste` command to paste items - into the hovered directory even when `smart_paste` is set to `false`. + into the hovered directory even + when `smart_paste` is set to `false`. This allows you to set a key to use this behaviour with the `paste` command instead of using it for every `paste` command. @@ -784,26 +840,31 @@ in your `keymap.toml` file. ### Quit (`quit`) -- You should use Yazi's default `quit` command instead of this augmented - command if you don't want to have a prompt when quitting Yazi - with multiple tabs open. - This command has a visual side effect of showing a confirmation prompt - for a split second before closing Yazi when quitting Yazi - with only 1 tab open, which can be annoying. - This confirmation prompt is due to the plugin still running for a bit - after the `quit` command is emitted, causing Yazi to prompt you for +- You should use Yazi's default `quit` command instead of this + augmented command if you don't want to have a prompt + when quitting Yazi with multiple tabs open. + This command has a visual side effect of showing a + confirmation prompt for a split second before closing Yazi + when quitting Yazi with only 1 tab open, + which can be annoying. + This confirmation prompt is due to the plugin still running + for a bit after the `quit` command is emitted, + causing Yazi to prompt you for confirmation as there are tasks still running. - However, once the plugin has stopped running, which is a split second - after the `quit` command is emitted, Yazi will exit. + However, once the plugin has stopped running, + which is a split second after the `quit` command is emitted, + Yazi will exit. You can observe this visual effect in the video demonstration below. -- When `confirm_on_quit` is set to `true`, the plugin will prompt you for - confirmation when there is more than 1 tab open. Otherwise, it will - immediately quit Yazi, just like the default `quit` command. -- `--confirm` flag to get the plugin to prompt you for confirmation when - quitting with multiple tabs open. - This flag will cause the `quit` command to prompt you for confirmation - when quitting with multiple tabs open even when `confirm_on_quit` is - set to `false`. +- When `confirm_on_quit` is set to `true`, + the plugin will prompt you for + confirmation when there is more than 1 tab open. + Otherwise, it will immediately quit Yazi, + just like the default `quit` command. +- `--confirm` flag to get the plugin to prompt you + for confirmation when quitting with multiple tabs open. + This flag will cause the `quit` command to + prompt you for confirmation when quitting with multiple tabs open + even when `confirm_on_quit` is set to `false`. This allows you to set a specific key to use this behaviour with the `quit` command instead of using it for every `quit` command. @@ -828,14 +889,18 @@ in your `keymap.toml` file. [wraparound-arrow-video] -- When both `smooth_scrolling` and `wraparound_file_navigation` are set to - `true`, the command will smoothly scroll the wraparound transition as well. +- When both `smooth_scrolling` and `wraparound_file_navigation` + are set to `true`, + the command will smoothly scroll the wraparound transition as well. Video: [smooth-wraparound-arrow-video] -- Otherwise, it'll behave like the default `arrow` command. +- Otherwise, it'll behave like the default `arrow 1` command. +- `--no-wrap` flag to prevent the `arrow` command + from wrapping around, + even when `wraparound_file_navigation` is set to `true`. ## New commands @@ -865,8 +930,9 @@ in your `keymap.toml` file. [wraparound-parent-arrow-video] -- When both `smooth_scrolling` and `wraparound_file_navigation` are set to - `true`, the command will smoothly scroll the wraparound transition as well. +- When both `smooth_scrolling` and `wraparound_file_navigation` + are set to `true`, + the command will smoothly scroll the wraparound transition as well. Video: @@ -894,6 +960,71 @@ in your `keymap.toml` file. desc = "Move down in the parent directory" ``` +- `--no-wrap` flag to prevent the `parent_arrow` command from + wrapping around, + even when `wraparound_file_navigation` is set to `true`. + +### Archive (`archive`) + +- The `archive` command adds the selected or hovered items + to an archive, with the plugin prompting for an archive name. + The archive file extension given will be used to determine + the type of archive to create. +- This command is also augmented as stated in + [this section above][augment-section]. + + Videos: + + - When `must_have_hovered_item` is `true`: + + [archive-must-have-hovered-item-video] + + - When `must_have_hovered_item` is `false`: + + [archive-hovered-item-optional-video] + + - When `prompt` is set to `true`: + + [archive-prompt-video] + + - When `prompt` is set to `false`: + + [archive-behaviour-video] + +- `--encrypt` flag to encrypt the archive with the given password, + which applies even when `encrypt_archives` is set to `false`. +- `--encrypt-headers` flag to encrypt the archive headers, + which applies even when `encrypt_archive_headers` + is set to `false`. + Note that this option only works with `7z` archives, + other types of archives like `zip` archives + do not support header encryption. + The plugin will show a warning if the archive type + does not support header encryption and the flag is passed, + but will continue with the creation of the encrypted archive. + This option has no effect if either `encrypt_archives` + is set to `false` or the `--encrypt` flag isn't given. + + Video: + + [archive-encrypt-files-video] + +- `--reveal` flag to automatically hover the archive file + that is created, which applies even when + `reveal_created_archive` is set to `false`. + + Video: + + [archive-reveal-created-archive-video] + +- `--remove` flag to automatically remove the files + that are added to the archive, which applies even when + `remove_archived_files` is set to `false`. + + Video: + + [archive-remove-archived-files-video] + ### Editor (`editor`) - The `editor` command opens the default editor set by the @@ -1059,98 +1190,110 @@ You can view the full licence in the [`LICENSE`][Licence] file. -[open-prompt-video]: https://github.com/user-attachments/assets/ad51d25b-dc68-48f6-bd14-20dc0b68fb0c -[open-behaviour-video]: https://github.com/user-attachments/assets/0b84e0e8-e483-4c3c-8408-4f672a34e249 -[open-auto-extract-archives-video]: https://github.com/user-attachments/assets/ae7e8b33-1f41-4ee7-8f5b-18fb4d455709 -[open-recursively-extract-archives-video]: https://github.com/user-attachments/assets/99119e03-e770-4442-b529-c5586cc04bd0 +[open-prompt-video]: https://github.com/user-attachments/assets/624da7a1-99cc-428d-acbb-d4ce684f92cb +[open-behaviour-video]: https://github.com/user-attachments/assets/202f1c07-2001-49d7-a9d7-7a4e7b102c41 +[open-auto-extract-archives-video]: https://github.com/user-attachments/assets/dad0572e-15d7-4dfd-bf48-00851af66b6e +[open-recursively-extract-archives-video]: https://github.com/user-attachments/assets/b8e929ce-32cd-4f7e-ad97-ffe94bdacd71 -[extract-must-have-hovered-item-video]: https://github.com/user-attachments/assets/c7d4ef2f-5455-4c06-a84e-bfc71c31bfea -[extract-hovered-item-optional-video]: https://github.com/user-attachments/assets/64536ddb-2309-4442-b5a3-73b334d68161 -[extract-prompt-video]: https://github.com/user-attachments/assets/40ad3b74-0036-4835-bfd8-cec6259504c4 -[extract-behaviour-video]: https://github.com/user-attachments/assets/6c7e7b3e-1be5-42ab-b7d9-987dcc10cc88 -[extract-recursively-extract-archives-video]: https://github.com/user-attachments/assets/6b5aef9d-9673-458b-8555-0c84570656dd -[extract-encrypted-archive]: https://github.com/user-attachments/assets/9c1c1377-6693-409d-840e-1eb128cf3ccd +[extract-must-have-hovered-item-video]: https://github.com/user-attachments/assets/6b9f347f-34ce-4ca1-8e3a-24dbeb297dc7 +[extract-hovered-item-optional-video]: https://github.com/user-attachments/assets/1596f9f8-e0f5-49e3-964c-6f94b1c24055 +[extract-prompt-video]: https://github.com/user-attachments/assets/568aef53-dc90-420e-ada9-171f56388ee1 +[extract-behaviour-video]: https://github.com/user-attachments/assets/d6ebf5fb-45d3-4ae0-adab-3e89b1ec2f4e +[extract-recursively-extract-archives-video]: https://github.com/user-attachments/assets/d8978671-9149-45b7-9a92-21c1697baa7d +[extract-encrypted-archive-video]: https://github.com/user-attachments/assets/24701662-0c19-4478-b576-75d9284cd695 +[extract-reveal-extracted-item-video]: https://github.com/user-attachments/assets/170bf187-dc2a-4310-aa86-5dc9a864aced +[extract-remove-extracted-archive-video]: https://github.com/user-attachments/assets/9b2e625a-a7f8-4678-8a26-265a7d3b8e0c -[smart-enter-video]: https://github.com/user-attachments/assets/85b043bc-f152-44a9-9627-d0282a6481ef -[enter-skip-single-subdirectory-video]: https://github.com/user-attachments/assets/70b3f595-31f8-474d-a623-fc4d927566ec +[smart-enter-video]: https://github.com/user-attachments/assets/be950544-a58f-495a-a7e8-e37e99011070 +[enter-skip-single-subdirectory-video]: https://github.com/user-attachments/assets/1bd30cb0-611a-44f2-9839-968287af4455 -[leave-skip-single-subdirectory-video]: https://github.com/user-attachments/assets/3f0f98d1-519e-48c7-90d8-15e57986cc89 +[leave-skip-single-subdirectory-video]: https://github.com/user-attachments/assets/735de8f7-6abb-4929-8e3d-bba470fc6063 -[rename-must-have-hovered-item-video]: https://github.com/user-attachments/assets/0ec0ad97-d0f0-441d-86a7-73a93a11a683 -[rename-hovered-item-optional-video]: https://github.com/user-attachments/assets/9bc918fa-c8b6-4f09-954c-7631a61032a0 -[rename-prompt-video]: https://github.com/user-attachments/assets/69da784c-0408-468b-bc34-f2271d0a8cdc -[rename-behaviour-video]: https://github.com/user-attachments/assets/44ea4d01-3d66-4ecd-82ac-4b4f874d2124 +[rename-must-have-hovered-item-video]: https://github.com/user-attachments/assets/b0f608df-7e8d-4e5f-958f-b3df9d0a92fa +[rename-hovered-item-optional-video]: https://github.com/user-attachments/assets/9613d238-d8e3-4e6f-b073-f847a4f331a1 +[rename-prompt-video]: https://github.com/user-attachments/assets/6ad26e1b-dab1-4e8e-92af-bfcbf6c5232c +[rename-behaviour-video]: https://github.com/user-attachments/assets/fdd70919-f164-4676-9046-6a4b95cd86ed -[remove-must-have-hovered-item-video]: https://github.com/user-attachments/assets/76c511c8-d3b6-494c-9fcf-f4035325bca3 -[remove-hovered-item-optional-video]: https://github.com/user-attachments/assets/4502d34d-1432-43ee-99b1-234085f795b5 -[remove-prompt-video]: https://github.com/user-attachments/assets/9b7f774c-f982-4e57-a895-6368f3043311 -[remove-behaviour-video]: https://github.com/user-attachments/assets/de57f05d-582d-44d3-9908-5c3f370e7119 +[remove-must-have-hovered-item-video]: https://github.com/user-attachments/assets/fa0aeb4e-ee6a-4e34-9c6f-4d4bc0afd111 +[remove-hovered-item-optional-video]: https://github.com/user-attachments/assets/f42167dc-df59-451d-b26b-16e75be93fad +[remove-prompt-video]: https://github.com/user-attachments/assets/dd901db3-1ab4-4cb7-bb3f-8aec335c7b94 +[remove-behaviour-video]: https://github.com/user-attachments/assets/d4087826-3480-486d-98ff-708bca548270 -[create-and-enter-directories-video]: https://github.com/user-attachments/assets/70ffbdc9-d05f-4cb7-bf18-b219139221ab -[create-and-open-files-video]: https://github.com/user-attachments/assets/b3c4ae5a-41be-4d6a-8cb9-59450a4b77c4 -[create-and-open-files-and-directories-video]: https://github.com/user-attachments/assets/f645fa80-eb78-4bee-b234-a0fa8b1d5610 -[create-behaviour-video]: https://github.com/user-attachments/assets/0d73e2a1-ed13-4601-b9b5-3e696d69621c -[create-default-behaviour-video]: https://github.com/user-attachments/assets/452300b3-71d1-46ce-aa65-bed39ff2b92d +[create-and-enter-directories-video]: https://github.com/user-attachments/assets/0604e9a3-7c23-4e9c-963a-6e83955fe7ae +[create-and-open-files-video]: https://github.com/user-attachments/assets/2feee5e5-1d56-4150-8f08-101338392950 +[create-and-open-files-and-directories-video]: https://github.com/user-attachments/assets/629a6923-6fa5-4b85-93b7-3b5495c8bbbb +[create-behaviour-video]: https://github.com/user-attachments/assets/55b485ea-e49e-4629-b077-4f2a81031f38 +[create-default-behaviour-video]: https://github.com/user-attachments/assets/acf04af0-107f-4ebb-9ed2-2a03847071a5 -[shell-must-have-hovered-item-video]: https://github.com/user-attachments/assets/dec6a8f1-1a7a-4955-aab1-46bf185aa0c5 -[shell-hovered-item-optional-video]: https://github.com/user-attachments/assets/989a57d6-10ae-4e5d-93a4-d7461fef436f -[shell-prompt-video]: https://github.com/user-attachments/assets/5c6f92be-a21e-49fd-aec2-7bdde85fd21b -[shell-behaviour-video]: https://github.com/user-attachments/assets/dba3b896-377d-4cee-8168-792cbf4c0491 -[shell-exit-if-directory-video]: https://github.com/user-attachments/assets/0c01094d-5518-411c-965a-63a77ecaa910 +[shell-must-have-hovered-item-video]: https://github.com/user-attachments/assets/d9a49cb7-67fd-40fe-8f68-130561de0cca +[shell-hovered-item-optional-video]: https://github.com/user-attachments/assets/f7fdfa7c-d7cc-4157-bb5f-63c37f82ea5b +[shell-prompt-video]: https://github.com/user-attachments/assets/6648ad92-320f-457d-9f3e-b5da6f1c07bc +[shell-behaviour-video]: https://github.com/user-attachments/assets/4727658f-e27c-4fbe-acd7-acfeac54cd17 +[shell-exit-if-directory-video]: https://github.com/user-attachments/assets/ef0e9818-cc81-45fc-9126-6903314ed6b3 -[smart-paste-video]: https://github.com/user-attachments/assets/067ad79a-d224-475e-8333-c1bf34aea746 +[smart-paste-video]: https://github.com/user-attachments/assets/59800de2-8445-4f68-834b-10a0e394c400 -[smart-tab-create-video]: https://github.com/user-attachments/assets/f1805f59-cfd2-4d97-b1b7-a4de1a25b668 +[smart-tab-create-video]: https://github.com/user-attachments/assets/bf48e411-b7ed-4624-abd9-084dcdc25ab5 -[smart-tab-switch-video]: https://github.com/user-attachments/assets/de59fd6b-7dbe-4055-8a9f-174fd24d5a8c +[smart-tab-switch-video]: https://github.com/user-attachments/assets/5b94ff0f-f77c-4250-96ac-4fea67eebc46 -[quit-with-confirmation-video]: https://github.com/user-attachments/assets/a87cdc00-e22b-4b96-a069-229fbfd7d451 +[quit-with-confirmation-video]: https://github.com/user-attachments/assets/9e212a32-c7c3-470f-895e-bb1cba03292d -[smooth-arrow-video]: https://github.com/user-attachments/assets/d6c9bc96-5fdc-4ecd-8c30-91d3e97e67db -[wraparound-arrow-video]: https://github.com/user-attachments/assets/b52153df-1e7a-4063-8bcb-a7c4dc923652 -[smooth-wraparound-arrow-video]: https://github.com/user-attachments/assets/f9bdd784-1cae-4292-8809-af6e26d5860f +[smooth-arrow-video]: https://github.com/user-attachments/assets/990935f5-ce6e-4536-9ddc-d8a32da69803 +[wraparound-arrow-video]: https://github.com/user-attachments/assets/b8233e26-06b7-436d-bafa-1639ed12aa53 +[smooth-wraparound-arrow-video]: https://github.com/user-attachments/assets/59d3618a-59cd-448d-b552-e3cc796d109e -[parent-arrow-video]: https://github.com/user-attachments/assets/ea2d2b37-0355-466d-bbd5-0a5860507589 -[smooth-parent-arrow-video]: https://github.com/user-attachments/assets/b62548eb-2f10-4f15-8c95-9127b90a364c -[wraparound-parent-arrow-video]: https://github.com/user-attachments/assets/2fcc30b8-9a6a-44d2-84c4-d224e9c467d8 -[smooth-wraparound-parent-arrow-video]: https://github.com/user-attachments/assets/44b87884-58b7-41af-81e6-e4cf771665aa +[parent-arrow-video]: https://github.com/user-attachments/assets/c412ed7b-a6ee-44e7-bf68-f9f2e5214a50 +[smooth-parent-arrow-video]: https://github.com/user-attachments/assets/45fdfa5f-86ef-453c-89a8-1c58d6318e93 +[wraparound-parent-arrow-video]: https://github.com/user-attachments/assets/3a429a57-2dc6-47c6-be4e-fab5844bafde +[smooth-wraparound-parent-arrow-video]: https://github.com/user-attachments/assets/7d3d58ea-4b00-4cb2-9aea-950940b1ad1a + + + +[archive-must-have-hovered-item-video]: https://github.com/user-attachments/assets/a8d667c8-a8a5-44ed-9220-aa55345c98ed +[archive-hovered-item-optional-video]: https://github.com/user-attachments/assets/213f82d2-fe70-4c42-8f24-b09c7d7d7dfe +[archive-prompt-video]: https://github.com/user-attachments/assets/20366432-f195-4531-bb08-9b3f0e8f848e +[archive-behaviour-video]: https://github.com/user-attachments/assets/dcbe9909-db40-4f96-8591-c1a104b63381 +[archive-encrypt-files-video]: https://github.com/user-attachments/assets/08b98e01-2cc2-486e-bd4d-bfbe9da792db +[archive-reveal-created-archive-video]: https://github.com/user-attachments/assets/e908afb8-c431-4e6d-8834-c02216461076 +[archive-remove-archived-files-video]: https://github.com/user-attachments/assets/d0495292-6508-4417-b378-033e6acc0c4d -[editor-must-have-hovered-item-video]: https://github.com/user-attachments/assets/ba982592-3351-4f13-bdf3-8971e0fdaf2c -[editor-hovered-item-optional-video]: https://github.com/user-attachments/assets/0d9ba0fa-6ad3-492d-b3d4-3ea427ac1b9d -[editor-prompt-video]: https://github.com/user-attachments/assets/792ebdec-45c8-430c-a426-35c94adeb6ce -[editor-behaviour-video]: https://github.com/user-attachments/assets/3f1740af-334f-46a2-aa61-2d783bf82a6c +[editor-must-have-hovered-item-video]: https://github.com/user-attachments/assets/a0e4500d-7bfe-4fb1-9289-8c39c5c6f323 +[editor-hovered-item-optional-video]: https://github.com/user-attachments/assets/96798f1f-2ddc-464a-b6ae-48cc20f88b3c +[editor-prompt-video]: https://github.com/user-attachments/assets/5453cc87-3721-4b80-8cc2-b730ddff5da8 +[editor-behaviour-video]: https://github.com/user-attachments/assets/f479ef38-5b78-43f1-83d4-045bef6f5f20 -[pager-must-have-hovered-item-video]: https://github.com/user-attachments/assets/b992a0a1-eb07-4ee5-af2a-179b94bff764 -[pager-hovered-item-optional-video]: https://github.com/user-attachments/assets/7a35f938-687a-40b7-82bd-fde72c185edd -[pager-prompt-video]: https://github.com/user-attachments/assets/3ec2a4e4-bf44-4058-938f-d9af66e15191 -[pager-behaviour-video]: https://github.com/user-attachments/assets/7185799e-48bc-40c0-a11d-a94d0989d8d7 +[pager-must-have-hovered-item-video]: https://github.com/user-attachments/assets/7daa8e81-bc83-4ff3-9462-d71293565e67 +[pager-hovered-item-optional-video]: https://github.com/user-attachments/assets/e123fb58-d49e-46b6-bc33-c41eb01cf9aa +[pager-prompt-video]: https://github.com/user-attachments/assets/cfba76a1-116e-4caf-b25a-57d631d115a0 +[pager-behaviour-video]: https://github.com/user-attachments/assets/d036f865-2909-414b-80d9-80e550b6b3dd diff --git a/config/yazi/plugins/augment-command.yazi/main.lua b/config/yazi/plugins/augment-command.yazi/main.lua index 5e94deed..915e1ef6 100644 --- a/config/yazi/plugins/augment-command.yazi/main.lua +++ b/config/yazi/plugins/augment-command.yazi/main.lua @@ -1,4 +1,4 @@ ---- @since 25.3.2 +--- @since 25.4.8 -- Plugin to make some Yazi commands smarter -- Written in Lua 5.4 @@ -20,24 +20,32 @@ -- The type of the command table ---@alias CommandTable table --- The type for the extractor list items command ----@alias ExtractorListItemsCommand fun( ---- self: Extractor, +-- The type for the archiver list items command +---@alias Archiver.ListItemsCommand fun( +--- self: Archiver, ---): output: CommandOutput|nil, error: Error|nil --- The type for the extractor get items function ----@alias ExtractorGetItems fun( ---- self: Extractor, +-- The type for the archiver get items function +---@alias Archiver.GetItems fun( +--- self: Archiver, ---): files: string[], directories: string[], error: string|nil --- The type for the extractor extract function. ----@alias ExtractorExtract fun( ---- self: Extractor, +-- The type for the archiver extract function +---@alias Archiver.Extract fun( +--- self: Archiver, --- has_only_one_file: boolean|nil, ----): ExtractionResult +---): Archiver.Result --- The type for the extractor function ----@alias ExtractorCommand fun(): output: CommandOutput|nil, error: Error|nil +-- The type for the archiver archive function +---@alias Archiver.Archive fun( +--- self: Archiver, +--- item_paths: string[], +--- password: string|nil, +--- encrypt_headers: boolean|nil, +---): Archiver.Result + +-- The type for the archiver command function +---@alias Archiver.Command fun(): output: CommandOutput|nil, error: Error|nil -- Custom types @@ -57,6 +65,10 @@ ---@field enter_archives boolean Whether to enter archives ---@field extract_retries number How many times to retry extracting ---@field recursively_extract_archives boolean Extract inner archives or not +---@field encrypt_archives boolean Whether to encrypt created archives +---@field encrypt_archive_headers boolean Whether to encrypt archive headers +---@field reveal_created_archive boolean Whether to reveal the created archive +---@field remove_archived_files boolean Whether to remove archived files ---@field preserve_file_permissions boolean Whether to preserve file permissions ---@field must_have_hovered_item boolean Whether to stop when no item is hovered ---@field skip_single_subdirectory_on_enter boolean Skip single subdir on enter @@ -72,16 +84,16 @@ ---@class (exact) State ---@field config Configuration The configuration object --- The type for the extractor function result ----@class (exact) ExtractionResult ----@field successful boolean Whether the extractor function was successful ----@field output string|nil The output of the extractor function ----@field cancelled boolean|nil boolean Whether the extraction was cancelled +-- The type for the archiver function result +---@class (exact) Archiver.Result +---@field successful boolean Whether the archiver function was successful +---@field output string|nil The output of the archiver function +---@field cancelled boolean|nil boolean Whether the archiver was cancelled ---@field error string|nil The error message ---@field archive_path string|nil The path to the archive ---@field destination_path string|nil The path to the destination ---@field extracted_items_path string|nil The path to the extracted items ----@field extractor_name string|nil The name of the extractor +---@field archiver_name string|nil The name of the archiver -- The name of the plugin ---@type string @@ -104,6 +116,7 @@ local Commands = { Quit = "quit", Arrow = "arrow", ParentArrow = "parent_arrow", + Archive = "archive", Editor = "editor", Pager = "pager", } @@ -134,12 +147,16 @@ local DEFAULT_CONFIG = { extract_retries = 3, recursively_extract_archives = true, preserve_file_permissions = false, + encrypt_archives = false, + encrypt_archive_headers = false, + reveal_created_archive = true, + remove_archived_files = false, must_have_hovered_item = true, skip_single_subdirectory_on_enter = true, skip_single_subdirectory_on_leave = true, smooth_scrolling = false, scroll_delay = 0.02, - wraparound_file_navigation = false, + wraparound_file_navigation = true, } -- The default input options for this plugin @@ -181,9 +198,9 @@ local INPUT_OPTIONS_TABLE = { [ItemGroup.None] = "(h/s)", } --- The extractor names ----@enum ExtractorName -local ExtractorName = { +-- The archiver names +---@enum ArchiverName +local ArchiverName = { SevenZip = "7-Zip", Tar = "Tar", } @@ -195,7 +212,7 @@ local ExtractBehaviour = { Rename = "rename", } --- The list of archive file extensions +-- The table of archive file extensions ---@type table local ARCHIVE_FILE_EXTENSIONS = { ["7z"] = true, @@ -221,40 +238,46 @@ local ARCHIVE_FILE_EXTENSIONS = { zip = true, } --- The error for the base extractor class +-- The table of archive file extensions that +-- supports header encryption +local ARCHIVE_FILE_EXTENSIONS_WITH_HEADER_ENCRYPTION = { + ["7z"] = true, +} + +-- The error for the base archiver class -- which is an abstract base class that -- does not implement any functionality ---@type string -local BASE_EXTRACTOR_ERROR = table.concat({ +local BASE_ARCHIVER_ERROR = table.concat({ "The Extractor class is does not implement any functionality.", "How did you even manage to get here?", }, "\n") -- Class definitions --- The base extractor that all extractors inherit from ----@class Extractor ----@field name string The name of the extractor ----@field command string The shell command for the extractor ----@field commands string[] The possible extractor commands +-- The base archiver that all archivers inherit from +---@class Archiver +---@field name string The name of the archiver +---@field command string|nil The shell command for the archiver +---@field commands string[] The possible archiver commands --- ---- Whether the extractor supports preserving file permissions +--- Whether the archiver supports preserving file permissions ---@field supports_file_permissions boolean --- --- The map of the extract behaviour strings to the command flags ---@field extract_behaviour_map table -local Extractor = { +local Archiver = { name = "BaseExtractor", - command = "", + command = nil, commands = {}, supports_file_permissions = false, extract_behaviour_map = {}, } --- The function to create a subclass of the abstract base extractor +-- The function to create a subclass of the abstract base archiver ---@param subclass table The subclass to create ----@return Extractor subclass Subclass of the base extractor -function Extractor:subclass(subclass) +---@return Archiver subclass Subclass of the base archiver +function Archiver:subclass(subclass) -- -- Create a new instance @@ -268,23 +291,32 @@ function Extractor:subclass(subclass) end -- The method to get the archive items ----@type ExtractorGetItems -function Extractor:get_items() return {}, {}, BASE_EXTRACTOR_ERROR end +---@type Archiver.GetItems +function Archiver:get_items() return {}, {}, BASE_ARCHIVER_ERROR end -- The method to extract the archive ----@type ExtractorExtract -function Extractor:extract(_) +---@type Archiver.Extract +function Archiver:extract(_) return { successful = false, - error = BASE_EXTRACTOR_ERROR, + error = BASE_ARCHIVER_ERROR, + } +end + +-- The method to add items to an archive +---@type Archiver.Archive +function Archiver:archive(_) + return { + successful = false, + error = BASE_ARCHIVER_ERROR, } end -- The 7-Zip extractor ----@class SevenZip: Extractor +---@class SevenZip: Archiver ---@field password string The password to the archive -local SevenZip = Extractor:subclass({ - name = ExtractorName.SevenZip, +local SevenZip = Archiver:subclass({ + name = ArchiverName.SevenZip, commands = { "7z", "7zz" }, -- https://documentation.help/7-Zip/overwrite.htm @@ -297,9 +329,9 @@ local SevenZip = Extractor:subclass({ }) -- The Tar extractor ----@class Tar: Extractor -local Tar = Extractor:subclass({ - name = ExtractorName.Tar, +---@class Tar: Archiver +local Tar = Archiver:subclass({ + name = ArchiverName.Tar, commands = { "gtar", "tar" }, supports_file_permissions = true, @@ -314,20 +346,20 @@ local Tar = Extractor:subclass({ }) -- The default extractor, which is set to 7-Zip ----@class DefaultExtractor: SevenZip -local DefaultExtractor = SevenZip:subclass({}) +---@class DefaultArchiver: SevenZip +local DefaultArchiver = SevenZip:subclass({}) -- The table of archive mime types ----@type table -local ARCHIVE_MIME_TYPE_TO_EXTRACTOR_MAP = { - ["application/zip"] = DefaultExtractor, - ["application/gzip"] = DefaultExtractor, +---@type table +local ARCHIVE_MIME_TYPE_TO_ARCHIVER_MAP = { + ["application/zip"] = DefaultArchiver, + ["application/gzip"] = DefaultArchiver, ["application/tar"] = Tar, - ["application/bzip"] = DefaultExtractor, - ["application/bzip2"] = DefaultExtractor, - ["application/7z-compressed"] = DefaultExtractor, - ["application/rar"] = DefaultExtractor, - ["application/xz"] = DefaultExtractor, + ["application/bzip"] = DefaultArchiver, + ["application/bzip2"] = DefaultArchiver, + ["application/7z-compressed"] = DefaultArchiver, + ["application/rar"] = DefaultArchiver, + ["application/xz"] = DefaultArchiver, } -- Patterns @@ -348,10 +380,6 @@ local MIME_TYPE_PREFIXES_TO_REMOVE = { local get_mime_type_without_prefix_template_pattern = "^(%%a-)/%s([%%-%%d%%a]-)$" --- The pattern to get the file extension ----@type string -local file_extension_pattern = "%.([%a]+)$" - -- The pattern to get the shell variables in a command ---@type string local shell_variable_pattern = "[%$%%][%*@0]" @@ -372,19 +400,56 @@ local bat_command_pattern = "%f[%a]bat%f[%A]" -- with the items in the first table being added first, -- and the items in the second table being added second, -- and so on. +-- +-- Pass true as the first parameter to get the function +-- to merge the tables recursively. +---@param deep_or_target table|boolean Whether to merge recursively +---@param target table The target table to merge ---@param ... table[] The tables to merge ---@return table merged_table The merged table -local function merge_tables(...) +local function merge_tables(deep_or_target, target, ...) -- - -- Initialise a new table - local new_table = {} + -- Initialise the target table + local target_table = nil + + -- Initialise the arguments + local args = nil + + -- Initialise the recursive variable + local recursive = false + + -- If the deep or target variable not a table + if type(deep_or_target) ~= "table" then + -- + + -- Set the recursive variable to the boolean value of the + -- deep or target variable + recursive = not not deep_or_target + + -- Set the target table to the target variable + target_table = target + + -- Set the arguments to the rest of the arguments + args = { ... } + + -- Otherwise, the deep or target variable is a table + else + -- + + -- Set the target table to the deep or target variable + target_table = deep_or_target + + -- Set the arguments to the target variable + -- and the rest of the arguments + args = { target, ... } + end -- Initialise the index variable - local index = 1 + local index = #target_table + 1 -- Iterates over the tables given - for _, table in ipairs({ ... }) do + for _, table in ipairs(args) do -- -- Iterate over all of the keys and values @@ -398,23 +463,44 @@ local function merge_tables(...) -- -- Set the value mapped to the index - new_table[index] = value + target_table[index] = value -- Increment the index index = index + 1 - -- Otherwise, the key isn't a number - else + -- Continue the loop + goto continue + end + + -- If recursive merging is wanted + -- and the key for the target table + -- and the value are both tables + if + recursive + and type(target_table[key]) == "table" + and type(value) == "table" + then -- - -- Set the key in the new table to the value given - new_table[key] = value + -- Call the merge table function + -- recursively on the target table's + -- key to merge the table recursively + merge_tables(target_table[key], value) + + -- Continue the loop + goto continue end + + -- Otherwise, set the key in the target table to the value given + target_table[key] = value + + -- The label to continue the loop + ::continue:: end end - -- Return the new table - return new_table + -- Return the target table + return target_table end -- Function to split a string into a list @@ -578,10 +664,12 @@ end ---@param options YaziNotificationOptions|nil Options for the notification ---@return nil local function show_warning(warning_message, options) - return ya.notify(merge_tables(DEFAULT_NOTIFICATION_OPTIONS, options or {}, { - content = tostring(warning_message), - level = "warn", - })) + return ya.notify( + merge_tables({}, DEFAULT_NOTIFICATION_OPTIONS, options or {}, { + content = tostring(warning_message), + level = "warn", + }) + ) end -- Function to show an error @@ -589,10 +677,12 @@ end ---@param options YaziNotificationOptions|nil Options for the notification ---@return nil local function show_error(error_message, options) - return ya.notify(merge_tables(DEFAULT_NOTIFICATION_OPTIONS, options or {}, { - content = tostring(error_message), - level = "error", - })) + return ya.notify( + merge_tables({}, DEFAULT_NOTIFICATION_OPTIONS, options or {}, { + content = tostring(error_message), + level = "error", + }) + ) end -- Function to get the user's input @@ -601,11 +691,71 @@ end ---@return string|nil user_input The user's input ---@return InputEvent event The event for the input function local function get_user_input(prompt, options) - return ya.input(merge_tables(DEFAULT_INPUT_OPTIONS, options or {}, { + return ya.input(merge_tables({}, DEFAULT_INPUT_OPTIONS, options or {}, { title = prompt, })) end +-- Function to get a password from the user +---@param prompt string The prompt to show to the user +---@param confirmation_prompt string|nil The confirmation prompt +---@param options YaziInputOptions|nil Options for the input +---@return string|nil password The password or nil if the user cancelled +---@return InputEvent|nil event The event for the input function +local function get_password(prompt, confirmation_prompt, options) + -- + + -- If reconfirmation for the password is not wanted, + -- just obtain the user's password and return it + if not confirmation_prompt then return get_user_input(prompt, options) end + + -- Otherwise, initialise the password and the event + local password = nil + local event = nil + + -- While the password isn't set + while not password do + -- + + -- Get the initial password from the user + local initial_password, initial_event = get_user_input(prompt, options) + + -- If the initial password is nil, exit the function + if initial_password == nil then + return initial_password, initial_event + end + + -- Get the confirmation password from the user + local confirmation_password, confirmation_event = + get_user_input(confirmation_prompt, options) + + -- If the confirmation password is nil, exit the function + if confirmation_password == nil then + return confirmation_password, confirmation_event + end + + -- If the initial password and the confirmation password matches + if initial_password == confirmation_password then + -- + + -- Set the password to the confirmation password + password = confirmation_password + + -- Set the event to the confirmation event + event = confirmation_event + + -- Break out of the loop + break + end + + -- Otherwise, tell the user their passwords don't match + show_error("Passwords do not match") + end + + -- Return the password and event + return password, event +end + -- Function to get the user's confirmation ---@param title string|ui.Line The title of the confirmation prompt ---@param content string|ui.Text The content of the confirmation prompt @@ -614,7 +764,7 @@ local function get_user_confirmation(title, content) -- -- Get the user's confirmation - local confirmation = ya.confirm(merge_tables(DEFAULT_CONFIRM_OPTIONS, { + local confirmation = ya.confirm(merge_tables({}, DEFAULT_CONFIRM_OPTIONS, { title = title, content = content, })) @@ -716,14 +866,26 @@ local function async_shell_command_exists(shell_command, args) return output ~= nil end --- Function to emit a plugin command ----@param command string The plugin command to emit ----@param args Arguments The arguments to pass to the plugin command +-- Function to emit a command from this plugin +---@param command string The augmented command to emit +---@param args Arguments|string The arguments to pass to the augmented command ---@return nil -local function emit_plugin_command(command, args) +local function emit_augmented_command(command, args) + -- + + -- Initialise the arguments + local arguments = args + + -- If the arguments are passed in a table, + -- convert them to a string + if type(args) == "table" then + arguments = convert_arguments_to_string(args) + end + + -- Emit the augmented command return ya.mgr_emit("plugin", { PLUGIN_NAME, - string.format("%s %s", command, convert_arguments_to_string(args)), + string.format("%s %s", command, arguments), }) end @@ -743,7 +905,7 @@ local subscribe_to_augmented_extract_event = ya.sync(function(_) -- Emit the command to call the plugin's extract function -- with the given arguments and flags - emit_plugin_command("extract", { + emit_augmented_command("extract", { archive_path = ya.quote(arg), }) end @@ -812,7 +974,7 @@ local function is_archive_mime_type(mime_type) -- Get the archive extractor for the mime type local archive_extractor = - ARCHIVE_MIME_TYPE_TO_EXTRACTOR_MAP[standardised_mime_type] + ARCHIVE_MIME_TYPE_TO_ARCHIVER_MAP[standardised_mime_type] -- Return if an extractor exists for the mime type return archive_extractor ~= nil @@ -895,11 +1057,9 @@ local function get_temporary_directory_url(path, destination_given) -- -- Get the url of the path given - ---@type Url local path_url = Url(path) -- Initialise the parent directory to be the path given - ---@type Url local parent_directory_url = path_url -- If the destination is not given @@ -907,7 +1067,7 @@ local function get_temporary_directory_url(path, destination_given) -- -- Get the parent directory of the given path - parent_directory_url = Url(path):parent() + parent_directory_url = Url(path).parent -- If the parent directory doesn't exist, return nil if not parent_directory_url then return nil end @@ -1044,22 +1204,18 @@ local get_tab_preferences = ya.sync(function(_) return tab_preferences end) --- [TODO]: Remove the stage.is_loading once stage() is stable --- https://github.com/sxyazi/yazi/issues/2545 --- -- Function to get if Yazi is loading ---@type fun(): boolean -local yazi_is_loading = ya.sync(function(_) +local yazi_loaded = ya.sync(function(_) local stage = cx.active.current.stage - local is_func, loaded = pcall(stage) - if not is_func then return stage.is_loading end - return not loaded + local loaded, _ = stage() + return loaded end) -- Function to wait until Yazi is loaded ---@return nil local function wait_until_yazi_is_loaded() - while yazi_is_loading() do + while not yazi_loaded() do end end @@ -1285,35 +1441,40 @@ end -- The function to create a new instance of the extractor ---@param archive_path string The path to the archive ----@param destination_path string|nil The path to extract to ---@param config Configuration The configuration object ----@return Extractor|nil instance An instance of the extractor if available -function Extractor:new(archive_path, destination_path, config) +---@param destination_path string|nil The path to extract to +---@return Archiver|nil instance An instance of the extractor if available +function Archiver:new(archive_path, config, destination_path) -- -- Initialise whether the extractor is available - local available = false + local available = self.command ~= nil - -- Iterate over the commands - for _, command in ipairs(self.commands) do + -- If the extractor has not been initialised + if not available then -- - -- Call the shell command exists function - -- on the command - local exists = async_shell_command_exists(command) - - -- If the command exists - if exists then + -- Iterate over the commands + for _, command in ipairs(self.commands) do -- - -- Save the command - self.command = command + -- Call the shell command exists function + -- on the command + local exists = async_shell_command_exists(command) - -- Set the available variable to true - available = true + -- If the command exists + if exists then + -- - -- Break out of the loop - break + -- Save the command + self.command = command + + -- Set the available variable to true + available = true + + -- Break out of the loop + break + end end end @@ -1338,9 +1499,9 @@ end -- Function to retry the extractor ---@private ----@param extractor_function ExtractorCommand Extractor command to retry +---@param extractor_function Archiver.Command Extractor command to retry ---@param clean_up_wanted boolean|nil Whether to clean up the destination path ----@return ExtractionResult result Result of the extractor function +---@return Archiver.Result result Result of the extractor function function SevenZip:retry_extractor(extractor_function, clean_up_wanted) -- @@ -1349,11 +1510,10 @@ function SevenZip:retry_extractor(extractor_function, clean_up_wanted) local total_number_of_tries = self.config.extract_retries + 1 -- Get the url of the archive - ---@type Url local archive_url = Url(self.archive_path) -- Get the archive name - local archive_name = archive_url:name() + local archive_name = archive_url.name -- If the archive name is nil, -- return the result of the extractor function @@ -1449,16 +1609,17 @@ function SevenZip:retry_extractor(extractor_function, clean_up_wanted) input_width = #password_prompt + 1 end - -- Get the new position object - -- for the new input element - ---@type Position - local new_position = - merge_tables(DEFAULT_INPUT_OPTIONS.position, { w = input_width }) - -- Ask the user for the password - local user_input, event = - ---@diagnostic disable-next-line: missing-fields - get_user_input(password_prompt, { position = new_position }) + local user_input, event = get_password( + password_prompt, + nil, + merge_tables( + true, + {}, + DEFAULT_INPUT_OPTIONS, + { position = { w = input_width } } + ) + ) -- If the user has confirmed the input, -- and the user input is not nil, @@ -1494,7 +1655,7 @@ function SevenZip:retry_extractor(extractor_function, clean_up_wanted) end -- Function to list the archive items with the command ----@type ExtractorListItemsCommand +---@type Archiver.ListItemsCommand function SevenZip:list_items_command() -- @@ -1526,7 +1687,7 @@ function SevenZip:list_items_command() end -- Function to get the items in the archive ----@type ExtractorGetItems +---@type Archiver.GetItems function SevenZip:get_items() -- @@ -1658,7 +1819,7 @@ function SevenZip:extract_command(extract_files_only, extract_behaviour) "-o" .. self.destination_path, } - -- Return the command to extract the archive + -- Return the output of the command return Command(self.command) :args(arguments) :stdout(Command.PIPED) @@ -1667,7 +1828,7 @@ function SevenZip:extract_command(extract_files_only, extract_behaviour) end -- Function to extract the archive ----@type ExtractorExtract +---@type Archiver.Extract function SevenZip:extract(has_only_one_file) -- @@ -1681,8 +1842,81 @@ function SevenZip:extract(has_only_one_file) return result end +-- Function to call the command to add items to an archive +---@param item_paths string[] The path to the items being added to the archive +---@param password string|nil The password to encrypt the archive with +---@param encrypt_headers boolean|nil Whether to encrypt the archive headers +---@return CommandOutput|nil output The output of the command +---@return Error|nil error The error if any +function SevenZip:archive_command(item_paths, password, encrypt_headers) + -- + + -- Initialise the arguments for the command + local arguments = { + + -- Add to the archive + "a", + + -- Use UTF-8 encoding for console input and output + "-sccUTF-8", + } + + -- If the password is given, add the password + if password then table.insert(arguments, "-p" .. password) end + + -- If encrypting headers is wanted, + -- add the argument to encrypt the headers + if encrypt_headers then table.insert(arguments, "-mhe") end + + -- Add the archive path and the item paths + merge_tables(arguments, { + self.archive_path, + table.unpack(item_paths), + }) + + -- Return the output of the command + return Command(self.command) + :args(arguments) + :stdout(Command.PIPED) + :stderr(Command.PIPED) + :output() +end + +-- Function to add items to an archive +---@type Archiver.Archive +function SevenZip:archive(item_paths, password, encrypt_headers) + -- + + -- Get the output of the command + local output, error = + self:archive_command(item_paths, password, encrypt_headers) + + -- If there is no output, return the archiver result + if not output then + return { + successful = false, + error = tostring(error), + } + end + + -- If the output status code is not 0 + -- return the archiver result + if output.status.code ~= 0 then + return { + successful = false, + error = tostring(output.stderr), + } + end + + -- Otherwise, return successful and the archive path + return { + successful = true, + archive_path = self.archive_path, + } +end + -- Function to list the archive items with the command ----@type ExtractorListItemsCommand +---@type Archiver.ListItemsCommand function Tar:list_items_command() -- @@ -1708,7 +1942,7 @@ function Tar:list_items_command() end -- Function to get the items in the archive ----@type ExtractorGetItems +---@type Archiver.GetItems function Tar:get_items() -- @@ -1822,7 +2056,7 @@ end -- Tar automatically decompresses and extracts the archive -- in one command, so there's no need to run it twice to -- extract compressed tarballs. ----@type ExtractorExtract +---@type Archiver.Extract function Tar:extract(_) -- @@ -1855,71 +2089,135 @@ function Tar:extract(_) } end +-- Function to call the command to add items to an archive +---@param item_paths string[] The path to the items being added to the archive +function Tar:archive_command(item_paths) + -- + + -- Initialise the arguments to the command + local arguments = { + + -- Add the items to an archive + "-rf", + + -- The archive path + self.archive_path, + + -- The item paths + table.unpack(item_paths), + } + + -- Return the output of the command + return Command(self.command) + :args(arguments) + :stdout(Command.PIPED) + :stderr(Command.PIPED) + :output() +end + +-- Function to add items to an archive +---@type Archiver.Archive +function Tar:archive(item_paths) + -- + + -- Get the output of the command + local output, error = self:archive_command(item_paths) + + -- If there is no output, return the archiver result + if not output then + return { + successful = false, + error = tostring(error), + } + end + + -- If the output status code is not 0 + -- return the archiver result + if output.status.code ~= 0 then + return { + successful = false, + error = tostring(output.stderr), + } + end + + -- Otherwise, return successful and the archive path + return { + successful = true, + archive_path = self.archive_path, + } +end + -- Functions for the commands --- Function to get the extractor for the file type +-- Function to get the archiver for the file type ---@param archive_path string The path to the archive file ----@param destination_path string The path to the destination directory +---@param command SupportedCommands The command the archiver is used for ---@param config Configuration The configuration for the plugin ----@return ExtractionResult result The results of getting the extractor ----@return Extractor|nil extractor The extractor for the file type -local function get_extractor(archive_path, destination_path, config) +---@param destination_path string|nil The path to the destination directory +---@return Archiver|nil archiver The archiver for the file type +---@return Archiver.Result result The results of getting the archiver +local function get_archiver(archive_path, command, config, destination_path) -- -- Get the mime type of the archive file local mime_type = get_mime_type(archive_path) - -- Get the extractor for the mime type - local extractor = ARCHIVE_MIME_TYPE_TO_EXTRACTOR_MAP[mime_type] + -- Get the archiver for the mime type + local archiver = command == Commands.Archive and DefaultArchiver + or ARCHIVE_MIME_TYPE_TO_ARCHIVER_MAP[mime_type] - -- If there is no extractor, + -- If there is no archiver, -- return that it is not successful, -- but that it has been cancelled -- as the mime type is not an archive - if not extractor then - return { + if not archiver then + return archiver, { successful = false, cancelled = true, } end - -- Instantiate an instance of the extractor - local extractor_instance = - extractor:new(archive_path, destination_path, config) + -- Instantiate an instance of the archiver + local archiver_instance = + archiver:new(archive_path, config, destination_path) -- While the extractor instance failed to be created - while not extractor_instance do + while not archiver_instance do -- - -- If the extractor instance is the default extractor, + -- If the archiver instance is the default archiver, -- then return an error telling the user to install the -- default extractor - if extractor.name == DefaultExtractor.name then - return { - successful = false, - error = table.concat({ - string.format( - "%s is not installed,", - DefaultExtractor.name - ), - "please install it before using the 'extract' command", - }, " "), - } + if archiver.name == DefaultArchiver.name then + return archiver_instance, + { + successful = false, + error = table.concat({ + string.format( + "%s is not installed,", + DefaultArchiver.name + ), + string.format( + "please install it before using the '%s' command", + command + ), + }, " "), + } end - -- Try instantiating the default extractor - extractor_instance = - DefaultExtractor:new(archive_path, destination_path, config) + -- Try instantiating the default archiver + archiver_instance = + DefaultArchiver:new(archive_path, config, destination_path) end -- If the user wants to preserve file permissions, - -- and the target extractor for the mime type supports - -- preserving file permissions, but the extractor + -- and the target archiver for the mime type supports + -- preserving file permissions, but the archiver -- instantiated does not, show a warning to the user if config.preserve_file_permissions - and extractor.supports_file_permissions - and not extractor_instance.supports_file_permissions + and archiver.supports_file_permissions + and not archiver_instance.supports_file_permissions then -- @@ -1927,12 +2225,12 @@ local function get_extractor(archive_path, destination_path, config) local warning = table.concat({ string.format( "%s is not installed, defaulting to %s.", - extractor.name, - extractor_instance.name + archiver.name, + archiver_instance.name ), string.format( "However, %s does not support preserving file permissions.", - extractor_instance.name + archiver_instance.name ), }, "\n") @@ -1941,13 +2239,13 @@ local function get_extractor(archive_path, destination_path, config) end -- Return the extractor instance - return { successful = true }, extractor_instance + return archiver_instance, { successful = true } end -- Function to move the extracted items out of the temporary directory ---@param archive_url Url The url of the archive ---@param destination_url Url The url of the destination ----@return ExtractionResult result The result of the move +---@return Archiver.Result result The result of the move local function move_extracted_items(archive_url, destination_url) -- @@ -1955,7 +2253,7 @@ local function move_extracted_items(archive_url, destination_url) -- and return the extractor result in the event of an error ---@param error string The error message to return ---@param empty_dir_only boolean|nil Whether to remove the empty dir only - ---@return ExtractionResult + ---@return Archiver.Result local function fail(error, empty_dir_only) -- @@ -1963,7 +2261,7 @@ local function move_extracted_items(archive_url, destination_url) fs.remove(empty_dir_only and "dir" or "dir_all", destination_url) -- Return the extractor result - ---@type ExtractionResult + ---@type Archiver.Result return { successful = false, error = error, @@ -1994,7 +2292,7 @@ local function move_extracted_items(archive_url, destination_url) end -- Get the parent directory of the destination - local parent_directory_url = destination_url:parent() + local parent_directory_url = destination_url.parent -- If the parent directory doesn't exist, -- clean up and return the error @@ -2003,7 +2301,7 @@ local function move_extracted_items(archive_url, destination_url) end -- Get the name of the archive without the extension - local archive_name = archive_url:stem() + local archive_name = archive_url.stem -- If the name of the archive doesn't exist, -- clean up and return the error @@ -2030,7 +2328,7 @@ local function move_extracted_items(archive_url, destination_url) only_one_item = true -- Get the name of the first extracted item - local first_extracted_item_name = first_extracted_item.url:name() + local first_extracted_item_name = first_extracted_item.url.name -- If the first extracted item has no name, -- then clean up and return the error @@ -2094,7 +2392,7 @@ end ---@param args Arguments The arguments passed to the plugin ---@param config Configuration The configuration object ---@param destination_path string|nil The destination path to extract to ----@return ExtractionResult extraction_result The extraction results +---@return Archiver.Result extraction_result The extraction results local function recursively_extract_archive( archive_path, args, @@ -2124,13 +2422,17 @@ local function recursively_extract_archive( } end - -- Get an extractor for the archive - local get_extractor_result, extractor = - get_extractor(archive_path, tostring(temporary_directory_url), config) + -- Get an the archiver for the archive + local archiver, get_archiver_result = get_archiver( + archive_path, + Commands.Extract, + config, + tostring(temporary_directory_url) + ) - -- If there is no extractor, return the result - if not extractor then - return merge_tables(get_extractor_result, { + -- If there is no archiver, return the result + if not archiver then + return merge_tables({}, get_archiver_result, { archive_path = archive_path, destination_path = destination_path, }) @@ -2141,26 +2443,26 @@ local function recursively_extract_archive( -- - The archive path -- - The destination path -- - The name of the extractor - ---@param result ExtractionResult The result to add the paths to - ---@return ExtractionResult modified_result The result with the paths added + ---@param result Archiver.Result The result to add the paths to + ---@return Archiver.Result modified_result The result with the paths added local function add_additional_info(result) - return merge_tables(result, { + return merge_tables({}, result, { archive_path = archive_path, destination_path = destination_path, - extractor_name = extractor.name, + extractor_name = archiver.name, }) end -- Get the list of archive files and directories, -- the error message and the password - local archive_files, archive_directories, error = extractor:get_items() + local archive_files, archive_directories, error = archiver:get_items() -- If there are no are no archive files and directories if #archive_files == 0 and #archive_directories == 0 then -- -- The extraction result - ---@type ExtractionResult + ---@type Archiver.Result local extraction_result = { successful = false, error = error or "Archive is empty", @@ -2175,7 +2477,7 @@ local function recursively_extract_archive( and #archive_directories == 0 -- Extract the given archive - local extraction_result = extractor:extract(archive_has_only_one_file) + local extraction_result = archiver:extract(archive_has_only_one_file) -- If the extraction result is not successful, return it if not extraction_result.successful then @@ -2202,22 +2504,21 @@ local function recursively_extract_archive( end -- Get the url of the extracted items path - ---@type Url local extracted_items_url = Url(extracted_items_path) -- Initialise the base url for the extracted items local base_url = extracted_items_url -- Get the parent directory of the extracted items path - local parent_directory_url = extracted_items_url:parent() + local parent_directory_url = extracted_items_url.parent -- If the parent directory doesn't exist if not parent_directory_url then -- -- Modify the move result with a custom error - ---@type ExtractionResult - local modified_move_result = merge_tables(move_result, { + ---@type Archiver.Result + local modified_move_result = merge_tables({}, move_result, { error = "Archive has no parent directory", archive_path = archive_path, destination_path = destination_path, @@ -2240,7 +2541,7 @@ local function recursively_extract_archive( -- -- Get the file extension of the file - local file_extension = file:match(file_extension_pattern) + local file_extension = Url(file).ext -- If the file extension is not found, then skip the file if not file_extension then goto continue end @@ -2254,10 +2555,15 @@ local function recursively_extract_archive( -- Get the full path to the archive local full_archive_path = tostring(full_archive_url) + -- Yazi is now way too quick (a good problem to have, really), + -- so we slow it down a little to make sure that the + -- extracted files are not overwritten by each other + ya.sleep(10e-3) + -- Recursively extract the archive - emit_plugin_command( + emit_augmented_command( "extract", - merge_tables(args, { + merge_tables({}, args, { archive_path = ya.quote(full_archive_path), remove = true, }) @@ -2271,36 +2577,49 @@ local function recursively_extract_archive( return add_additional_info(move_result) end --- Function to show an extraction error ----@param extraction_result ExtractionResult The extraction result +-- Function to show an archiver error +---@param archiver_result Archiver.Result The result from the archiver ---@return nil -local function show_extraction_error(extraction_result) +local function show_archiver_error(archiver_result) -- -- The line for the error - local error_line = string.format("Error: %s", extraction_result.error) + local error_line = string.format("Error: %s", archiver_result.error) -- If the extractor name exists - if extraction_result.extractor_name then + if archiver_result.archiver_name then -- -- Add the extractor's name to the error error_line = string.format( "%s error: %s", - extraction_result.extractor_name, - extraction_result.error + archiver_result.archiver_name, + archiver_result.error ) end - -- Show the extraction error - return show_error(table.concat({ - string.format( - "Failed to extract archive at: %s", - extraction_result.archive_path - ), - string.format("Destination: %s", extraction_result.destination_path), - error_line, - }, "\n")) + -- Initialise the error + local error_string = nil + + -- If the destination path exists, + -- show the extraction error + if archiver_result.destination_path then + error_string = table.concat({ + string.format( + "Failed to extract archive at: %s", + archiver_result.archive_path + ), + string.format("Destination: %s", archiver_result.destination_path), + error_line, + }, "\n") + + -- Otherwise, just show the archiver error + else + error_string = error_line + end + + -- Show the error + show_error(error_string) end -- Function to handle the open command @@ -2331,7 +2650,7 @@ local function handle_open(args, config) -- calls the function to enter the directory -- and exit the function if config.smart_enter or table_pop(args, "smart", false) then - return emit_plugin_command("enter", args) + return emit_augmented_command("enter", args) end -- Otherwise, just exit the function @@ -2352,7 +2671,7 @@ local function handle_open(args, config) -- opening only the hovered item -- as the item group is the hovered item, -- and exit the function - return ya.mgr_emit("open", merge_tables(args, { hovered = true })) + return ya.mgr_emit("open", merge_tables({}, args, { hovered = true })) end -- Otherwise, the hovered item is an archive @@ -2364,17 +2683,16 @@ local function handle_open(args, config) if not archive_path then return end -- Get the parent directory of the hovered item - ---@type Url - local parent_directory_url = Url(archive_path):parent() + local parent_directory_url = Url(archive_path).parent -- If the parent directory doesn't exist, then exit the function if not parent_directory_url then return end -- Emit the command to extract the archive -- and reveal the extracted items - emit_plugin_command( + emit_augmented_command( "extract", - merge_tables(args, { + merge_tables({}, args, { archive_path = ya.quote(archive_path), reveal = true, parent_dir = ya.quote(tostring(parent_directory_url)), @@ -2451,9 +2769,9 @@ local function handle_extract(args, config) -- Iterate over the archive paths -- and call the extract command on them for _, archive_path in ipairs(archive_paths) do - emit_plugin_command( + emit_augmented_command( "extract", - merge_tables(args, { + merge_tables({}, args, { archive_path = ya.quote(archive_path), }) ) @@ -2483,7 +2801,7 @@ local function handle_extract(args, config) -- If the extraction is not successful, notify the user if not extraction_result.successful or not extracted_items_path then - return show_extraction_error(extraction_result) + return show_archiver_error(extraction_result) end -- Get the url of the archive @@ -2498,11 +2816,10 @@ local function handle_extract(args, config) -- -- Get the url of the extracted items - ---@type Url local extracted_items_url = Url(extracted_items_path) -- Get the parent directory of the extracted items - local parent_directory_url = extracted_items_url:parent() + local parent_directory_url = extracted_items_url.parent -- If the parent directory doesn't exist, then exit the function if not parent_directory_url then return end @@ -2566,7 +2883,7 @@ local function handle_enter(args, config) -- call the function for the open command -- and exit the function if config.smart_enter or table_pop(args, "smart", false) then - return emit_plugin_command("open", args) + return emit_augmented_command("open", args) end -- Otherwise, just exit the function @@ -2628,8 +2945,7 @@ local function handle_leave(args, config) if #directory_items ~= 1 then break end -- Get the parent directory of the current directory - ---@type Url|nil - local parent_directory = Url(directory):parent() + local parent_directory = Url(directory).parent -- If the parent directory is nil, -- break the loop @@ -2668,7 +2984,7 @@ local function handle_yazi_command(command, args) -- -- Emit the command with the hovered option - ya.mgr_emit(command, merge_tables(args, { hovered = true })) + ya.mgr_emit(command, merge_tables({}, args, { hovered = true })) end end @@ -2729,7 +3045,7 @@ local function execute_create(item_url, is_directory, args, config) -- -- Get the parent directory of the file to create - local parent_directory_url = item_url:parent() + local parent_directory_url = item_url.parent -- If the parent directory doesn't exist, -- then show an error and exit the function @@ -2797,7 +3113,6 @@ local function handle_create(args, config) if not user_input or event ~= 1 then return end -- Get the current working directory as a url - ---@type Url local current_working_directory = Url(get_current_directory()) -- Get whether the url ends with a path delimiter @@ -2808,7 +3123,6 @@ local function handle_create(args, config) local is_directory = ends_with_path_delimiter or dir_flag -- Get the url from the user's input - ---@type Url local item_url = Url(user_input) -- If the user does not want to use the default Yazi create behaviour @@ -2821,7 +3135,7 @@ local function handle_create(args, config) -- -- Get the file extension from the user's input - local file_extension = user_input:match(file_extension_pattern) + local file_extension = item_url.ext -- Set the is directory variable to the is directory condition -- or if the file extension exists @@ -3244,20 +3558,23 @@ local execute_tab_switch = ya.sync(function(state, args) -- Get the number of tabs currently open local number_of_open_tabs = #cx.tabs + -- Save the current tab's current working directory + local current_working_directory = tostring(current_tab.cwd) + -- Iterate from the number of current open tabs -- to the given tab number - for _ = number_of_open_tabs, tab_index do + for _ = number_of_open_tabs, tab_index - 1 do -- -- Call the tab create command - ya.mgr_emit("tab_create", { current_tab.cwd }) + ya.mgr_emit("tab_create", { current_working_directory }) -- If there is a hovered item if current_tab.hovered then -- -- Reveal the hovered item - ya.mgr_emit("reveal", { current_tab.hovered.url }) + ya.mgr_emit("reveal", { tostring(current_tab.hovered.url) }) end end @@ -3339,53 +3656,44 @@ local function smoothly_scroll(steps, scroll_delay, scroll_func) end -- Function to do the wraparound for the arrow command ----@type fun( ---- args: Arguments, -- The arguments passed to the plugin ----): nil -local wraparound_arrow = ya.sync(function(_, args) +---@param args Arguments -- The arguments passed to the plugin +---@return nil +local function wraparound_arrow(args) -- - -- Get the current tab - local current_tab = cx.active.current - -- Get the number of steps from the arguments given local steps = table.remove(args, 1) or 1 - -- If the step isn't a number, + -- If the number of steps isn't a number, -- immediately emit the arrow command -- and exit the function if type(steps) ~= "number" then - return ya.mgr_emit("arrow", merge_tables(args, { steps })) + return ya.mgr_emit("arrow", merge_tables({ steps }, args)) end - -- Get the number of files in the current tab - local number_of_files = #current_tab.files + -- Initialise the arrow command to use + local arrow_command = "next" - -- If there are no files in the current tab, exit the function - if number_of_files == 0 then return end + -- If the number of steps is negative, + if steps < 0 then + -- - -- Get the new cursor index, - -- which is the current cursor position plus the step given - -- to the arrow function, modulus the number of files in - -- the current tab - local new_cursor_index = (current_tab.cursor + steps) % number_of_files + -- Change the number of steps to positive + steps = -steps - -- Get the url of the item at the new cursor index. - -- - -- The plus one is needed to convert the cursor index, - -- which is 0-based, to a 1-based index, - -- which is what is used to index into the list of files. - local item_url = current_tab.files[new_cursor_index + 1].url + -- Set the arrow command to "prev" + arrow_command = "prev" + end - -- Emit the reveal command - ya.mgr_emit("reveal", merge_tables(args, { item_url })) -end) + -- Iterate over the number of steps + for _ = 1, steps do + -- + + -- Emit the arrow command + ya.mgr_emit("arrow", merge_tables({ arrow_command }, args)) + end +end --- [TODO]: Make use of the arrow prev and arrow next commands --- once stabilised. --- PR: https://github.com/sxyazi/yazi/pull/2485 --- Docs: https://yazi-rs.github.io/docs/configuration/keymap/#manager.arrow --- -- Function to handle the arrow command ---@type CommandFunction local function handle_arrow(args, config) @@ -3402,21 +3710,25 @@ local function handle_arrow(args, config) -- immediately emit the arrow command -- and exit the function if type(steps) ~= "number" then - return ya.mgr_emit("arrow", merge_tables(args, { steps })) + return ya.mgr_emit("arrow", merge_tables({ steps }, args)) end -- Initialise the function to the regular arrow command local function scroll_func(step) - ya.mgr_emit("arrow", merge_tables(args, { step })) + ya.mgr_emit("arrow", merge_tables({ step }, args)) end -- If wraparound file navigation is wanted - if config.wraparound_file_navigation then + -- and the no_wrap argument isn't passed + if + config.wraparound_file_navigation + and not table_pop(args, "no_wrap", false) + then -- - -- Set the scroll function to the wraparound arrow command + -- Use the wraparound arrow function function scroll_func(step) - wraparound_arrow(merge_tables(args, { step })) + wraparound_arrow(merge_tables({ step }, args)) end end @@ -3426,9 +3738,15 @@ local function handle_arrow(args, config) -- Otherwise, if smooth scrolling is not wanted, -- and wraparound file navigation is wanted, + -- and the no_wrap argument isn't passed, -- call the wraparound arrow function -- and exit the function - if config.wraparound_file_navigation then return wraparound_arrow(args) end + if + config.wraparound_file_navigation + and not table_pop(args, "no_wrap", false) + then + return wraparound_arrow(args) + end -- Otherwise, emit the regular arrow command ya.mgr_emit("arrow", args) @@ -3517,7 +3835,11 @@ local execute_parent_arrow = ya.sync(function(state, args) local sort_directories_first = cx.active.pref.sort_dir_first -- If wraparound file navigation is wanted - if state.config.wraparound_file_navigation then + -- and the no_wrap argument isn't passed + if + state.config.wraparound_file_navigation + and not table_pop(args, "no_wrap", false) + then -- -- If the user sorts their directories first @@ -3616,10 +3938,164 @@ local function handle_parent_arrow(args, config) smoothly_scroll( steps, config.scroll_delay, - function(step) execute_parent_arrow(merge_tables(args, { step })) end + function(step) execute_parent_arrow(merge_tables({ step }, args)) end ) end +-- Function to check if an archive supports header encryption +---@param archive_path string The path to the archive +---@param wanted boolean Whether header encryption is wanted +---@return boolean supports_header_encryption Header encryption supported or not +local function archive_supports_header_encryption(archive_path, wanted) + -- + + -- If header encryption isn't wanted, immediately return false + if not wanted then return false end + + -- Otherwise, get the extension of the archive + local archive_extension = Url(archive_path).ext + + -- If the extension doesn't support header encryption + local supports_header_encryption = + ARCHIVE_FILE_EXTENSIONS_WITH_HEADER_ENCRYPTION[archive_extension] + + -- If the archive extension does not support header encryption, + -- show a warning + if not supports_header_encryption then + show_warning(table.concat({ + string.format( + "'.%s' does not support header encryption,", + archive_extension + ), + "continuing archival process without header encryption.", + }, " ")) + end + + -- Return if the archive supports header encryption + return supports_header_encryption +end + +-- Function to remove files and directories +---@param item_paths string[] The paths to the items to remove +---@return nil +local function remove_items(item_paths) + -- + + -- Iterate over the item paths + for _, item_path in ipairs(item_paths) do + -- + + -- Get the url of the item + local item_url = Url(item_path) + + -- Get the cha of the item + local item_cha = fs.cha(item_url, false) + + -- If the item is a directory + if item_cha and item_cha.is_dir then + -- + + -- Remove everything + fs.remove("dir_all", item_url) + + -- Otherwise, remove the item + else + fs.remove("file", item_url) + end + end +end + +-- Function to handle the archive command +---@type CommandFunction +local function handle_archive(args, config) + -- + + -- Get the item group + local item_group = get_item_group() + + -- If there is no item group, exit the function + if not item_group then return end + + -- Initialise the paths to the items to add to the archive + local item_paths = nil + + -- If the item group is the selected items + if item_group == ItemGroup.Selected then + item_paths = get_paths_of_selected_items() + + -- Otherwise, the item group is the hovered item + else + -- + + -- Get the hovered item + local hovered_item_path = get_path_of_hovered_item() + + -- If the hovered item is nil somehow, then exit the function + if hovered_item_path == nil then return end + + -- Otherwise, set the item paths to the hovered item + item_paths = { hovered_item_path } + end + + -- If the item paths is nil, exit the function + if not item_paths then return end + + -- Get the path to the archive + local archive_path = get_user_input("Archive name:") + + -- If the archive path isn't given, exit the function + if not archive_path then return end + + -- Get the archiver + local archiver, get_archiver_results = + get_archiver(archive_path, Commands.Archive, config) + + -- If the archiver can't be instantiated, + -- show the error and exit the function + if not archiver then return show_archiver_error(get_archiver_results) end + + -- Initialise the password + local password = nil + + -- If the user wants to encrypt the archive, + -- get the password from the user + if config.encrypt_archives or table_pop(args, "encrypt", false) then + password = + get_password("Archive password:", "Confirm archive password:") + end + + -- Get whether to encrypt the headers or not + local encrypt_headers = archive_supports_header_encryption( + archive_path, + password + and ( + config.encrypt_archive_headers + or table_pop(args, "encrypt_headers", false) + ) + ) + + -- Call the function to add items to an archive + local archiver_result = + archiver:archive(item_paths, password, encrypt_headers) + + -- If the archiver is not successful, + -- show the error and exit the function + if not archiver_result.successful then + return show_archiver_error(archiver_result) + end + + -- If the user wants to reveal the created archive, + -- then reveal the created archive + if config.reveal_created_archive or table_pop(args, "reveal", false) then + ya.mgr_emit("reveal", { archive_path }) + end + + -- If the user wants to remove archived files, remove them + if config.remove_archived_files or table_pop(args, "remove", false) then + remove_items(item_paths) + end +end + -- Function to handle the editor command ---@type CommandFunction local function handle_editor(args, config) @@ -3691,6 +4167,7 @@ local function run_command_func(command, args, config) [Commands.Quit] = handle_quit, [Commands.Arrow] = handle_arrow, [Commands.ParentArrow] = handle_parent_arrow, + [Commands.Archive] = handle_archive, [Commands.Editor] = handle_editor, [Commands.Pager] = handle_pager, } diff --git a/config/yazi/plugins/glow.yazi/main.lua b/config/yazi/plugins/glow.yazi/main.lua index 09080154..8883feb4 100644 --- a/config/yazi/plugins/glow.yazi/main.lua +++ b/config/yazi/plugins/glow.yazi/main.lua @@ -39,13 +39,13 @@ function M:peek(job) child:start_kill() if job.skip > 0 and i < job.skip + limit then - ya.manager_emit("peek", { + ya.mgr_emit("peek", { tostring(math.max(0, i - limit)), only_if = job.file.url, upper_bound = true }) else - lines = lines:gsub("\t", string.rep(" ", PREVIEW.tab_size)) + lines = lines:gsub("\t", string.rep(" ", rt.preview.tab_size)) ya.preview_widgets(job, { ui.Text.parse(lines):area(job.area) }) end end @@ -55,7 +55,7 @@ function M:seek(job) if not h or h.url ~= job.file.url then return end - ya.manager_emit('peek', { + ya.mgr_emit('peek', { math.max(0, cx.active.preview.skip + job.units), only_if = job.file.url, }) diff --git a/config/yazi/plugins/hexyl.yazi/main.lua b/config/yazi/plugins/hexyl.yazi/main.lua index 3b6a51cf..35b02e4c 100644 --- a/config/yazi/plugins/hexyl.yazi/main.lua +++ b/config/yazi/plugins/hexyl.yazi/main.lua @@ -46,13 +46,13 @@ function M:peek(job) child:start_kill() if job.skip > 0 and i < job.skip + limit then - ya.manager_emit("peek", { + ya.mgr_emit("peek", { math.max(0, i - limit), only_if = job.file.url, upper_bound = true, }) else - lines = lines:gsub("\t", string.rep(" ", PREVIEW.tab_size)) + lines = lines:gsub("\t", string.rep(" ", rt.preview.tab_size)) ya.preview_widgets(job, { ui.Text.parse(lines):area(job.area) }) end end @@ -67,7 +67,7 @@ function M:seek(job) local scroll_offset = job.units -- Emit a new peek event with the updated skip value. - ya.manager_emit("peek", { + ya.mgr_emit("peek", { math.max(0, cx.active.preview.skip + scroll_offset), only_if = job.file.url, }) diff --git a/config/yazi/plugins/nbpreview.yazi/README.md b/config/yazi/plugins/nbpreview.yazi/README.md index 30782f7f..d1b65640 100644 --- a/config/yazi/plugins/nbpreview.yazi/README.md +++ b/config/yazi/plugins/nbpreview.yazi/README.md @@ -4,7 +4,7 @@ View your Jupyter notebooks beautifully in the preview in Yazi. ## Requirements -- [Yazi](https://github.com/sxyazi/yazi) version >=0.4 +- [Yazi](https://github.com/sxyazi/yazi) version >=25.4.8 - [nbpreview](https://github.com/paw-lu/nbpreview) ## Previews @@ -74,6 +74,18 @@ You can change the default give color scheme and theme to any you like. > > The loading of `ipynb` might appear slow. This is due to the lag created by the command itself and not because of the plugin or yazi +## Using piper.yazi + +[piper.yazi](https://github.com/yazi-rs/plugins/tree/main/piper.yazi) is a general-purpose previewer - you can pass any shell command to piper and it will use the command's output as the preview content. + +To use `nbpreview` with piper, you can add this in your `yazi.toml` file: + +```toml +[[plugin.prepend_previewers]] +name = "*.ipynb" +run = 'piper -- nbpreview --no-paging --nerd-font --decorated --no-files --unicode --color --images --color-system=standard --theme=ansi_dark "$1"' +``` + ## Explore Yazi Yazi is an amazing, blazing fast terminal file manager, with a variety of plugins, flavors and themes. Check them out at [awesome-yazi](https://github.com/AnirudhG07/awesome-yazi) and the official [yazi webpage](https://yazi-rs.github.io/). diff --git a/config/yazi/plugins/nbpreview.yazi/main.lua b/config/yazi/plugins/nbpreview.yazi/main.lua index 2a41cc35..79b9b3b5 100644 --- a/config/yazi/plugins/nbpreview.yazi/main.lua +++ b/config/yazi/plugins/nbpreview.yazi/main.lua @@ -22,6 +22,7 @@ function M:peek(job) :stdout(Command.PIPED) :stderr(Command.PIPED) :spawn() + if not child then return require("code"):peek(job) end @@ -44,10 +45,12 @@ function M:peek(job) child:start_kill() if job.skip > 0 and i < job.skip + limit then - ya.manager_emit("peek", { math.max(0, i - limit), only_if = job.file.url, upper_bound = true }) + ya.emit("peek", { math.max(0, i - limit), only_if = job.file.url, upper_bound = true }) else - lines = lines:gsub("\t", string.rep(" ", PREVIEW.tab_size)) - ya.preview_widgets(job, { ui.Text.parse(lines):area(job.area) }) + lines = lines:gsub("\t", string.rep(" ", rt.preview.tab_size)) + ya.preview_widgets(job, { + ui.Text.parse(lines):area(job.area):wrap(rt.preview.wrap == "yes" and ui.Text.WRAP or ui.Text.WRAP_NO), + }) end end diff --git a/config/yazi/plugins/ouch.yazi/README.md b/config/yazi/plugins/ouch.yazi/README.md index 0159e0d8..e09b3dfc 100644 --- a/config/yazi/plugins/ouch.yazi/README.md +++ b/config/yazi/plugins/ouch.yazi/README.md @@ -43,7 +43,8 @@ prepend_previewers = [ { mime = "application/x-bzip2", run = "ouch" }, { mime = "application/x-7z-compressed", run = "ouch" }, { mime = "application/x-rar", run = "ouch" }, - { mime = "application/x-xz", run = "ouch" }, + { mime = "application/x-xz", run = "ouch" }, + { mime = "application/xz", run = "ouch" }, ] ``` diff --git a/config/yazi/plugins/ouch.yazi/main.lua b/config/yazi/plugins/ouch.yazi/main.lua index e6cd980d..f642eb35 100644 --- a/config/yazi/plugins/ouch.yazi/main.lua +++ b/config/yazi/plugins/ouch.yazi/main.lua @@ -76,7 +76,7 @@ local get_compression_target = ya.sync(function() return end else - default_name = tab.current.cwd:name() + default_name = tab.current.cwd.name for _, url in pairs(tab.selected) do table.insert(paths, tostring(url)) end diff --git a/config/yazi/plugins/relative-motions.yazi/README.md b/config/yazi/plugins/relative-motions.yazi/README.md index 35a2386b..b950feaf 100644 --- a/config/yazi/plugins/relative-motions.yazi/README.md +++ b/config/yazi/plugins/relative-motions.yazi/README.md @@ -6,7 +6,7 @@ https://github.com/dedukun/relative-motions.yazi/assets/25795432/04fb186a-5efe-4 ## Requirements -- [Yazi](https://github.com/sxyazi/yazi) v25.2.7+ +- [Yazi](https://github.com/sxyazi/yazi) v25.4.8+ ## Installation diff --git a/config/yazi/plugins/relative-motions.yazi/main.lua b/config/yazi/plugins/relative-motions.yazi/main.lua index 2600a872..722325e7 100644 --- a/config/yazi/plugins/relative-motions.yazi/main.lua +++ b/config/yazi/plugins/relative-motions.yazi/main.lua @@ -1,4 +1,4 @@ ---- @since 25.2.7 +--- @since 25.4.8 -- stylua: ignore local MOTIONS_AND_OP_KEYS = { { on = "0" }, { on = "1" }, { on = "2" }, { on = "3" }, { on = "4" }, @@ -76,9 +76,9 @@ local render_motion = ya.sync(function(_, motion_num, motion_cmd) motion_span = ui.Span(string.format(" %3d%s ", motion_num, motion_cmd)) end - local status_config = THEME.status - local separator_open = status_config.separator_open or status_config.sep_right.open - local separator_close = status_config.separator_close or status_config.sep_right.close + local status_config = th.status + local separator_open = status_config.sep_right.open + local separator_close = status_config.sep_right.close return ui.Line { ui.Span(separator_open):fg(style.main.bg), @@ -124,13 +124,7 @@ local render_numbers = ya.sync(function(_, mode) local hovered_index for i, f in ipairs(files) do - -- TODO: Removed f:is_hovered() support in next Yazi release - if type(f.is_hovered) == "boolean" then - hovered = f.is_hovered - else - hovered = f:is_hovered() - end - if hovered then + if f.is_hovered then hovered_index = i break end @@ -274,8 +268,8 @@ return { if cmd == "g" then if direction == "g" then - ya.manager_emit("arrow", { "top" }) - ya.manager_emit("arrow", { lines - 1 }) + ya.mgr_emit("arrow", { "top" }) + ya.mgr_emit("arrow", { lines - 1 }) render_clear() return elseif direction == "j" then @@ -283,7 +277,7 @@ return { elseif direction == "k" then cmd = "k" elseif direction == "t" then - ya.manager_emit("tab_switch", { lines - 1 }) + ya.mgr_emit("tab_switch", { lines - 1 }) render_clear() return else @@ -294,66 +288,66 @@ return { end if cmd == "j" then - ya.manager_emit("arrow", { lines }) + ya.mgr_emit("arrow", { lines }) elseif cmd == "k" then - ya.manager_emit("arrow", { -lines }) + ya.mgr_emit("arrow", { -lines }) elseif cmd == "h" then for _ = 1, lines do - ya.manager_emit("leave", {}) + ya.mgr_emit("leave", {}) end elseif cmd == "l" then for _ = 1, lines do - ya.manager_emit("enter", {}) + ya.mgr_emit("enter", {}) local file_idx = get_cache_or_first_dir() if file_idx then - ya.manager_emit("arrow", { "top" }) - ya.manager_emit("arrow", { file_idx }) + ya.mgr_emit("arrow", { "top" }) + ya.mgr_emit("arrow", { file_idx }) end end elseif is_tab_command(cmd) then if cmd == "t" then for _ = 1, lines do - ya.manager_emit("tab_create", {}) + ya.mgr_emit("tab_create", {}) end elseif cmd == "H" then - ya.manager_emit("tab_switch", { -lines, relative = true }) + ya.mgr_emit("tab_switch", { -lines, relative = true }) elseif cmd == "L" then - ya.manager_emit("tab_switch", { lines, relative = true }) + ya.mgr_emit("tab_switch", { lines, relative = true }) elseif cmd == "w" then - ya.manager_emit("tab_close", { lines - 1 }) + ya.mgr_emit("tab_close", { lines - 1 }) elseif cmd == "W" then local curr_tab = get_active_tab() local del_tab = curr_tab + lines - 1 for _ = curr_tab, del_tab do - ya.manager_emit("tab_close", { curr_tab - 1 }) + ya.mgr_emit("tab_close", { curr_tab - 1 }) end - ya.manager_emit("tab_switch", { curr_tab - 1 }) + ya.mgr_emit("tab_switch", { curr_tab - 1 }) elseif cmd == "<" then - ya.manager_emit("tab_swap", { -lines }) + ya.mgr_emit("tab_swap", { -lines }) elseif cmd == ">" then - ya.manager_emit("tab_swap", { lines }) + ya.mgr_emit("tab_swap", { lines }) elseif cmd == "~" then local jump = lines - get_active_tab() - ya.manager_emit("tab_swap", { jump }) + ya.mgr_emit("tab_swap", { jump }) end else - ya.manager_emit("visual_mode", {}) + ya.mgr_emit("visual_mode", {}) -- invert direction when user specifies it if direction == "k" then - ya.manager_emit("arrow", { -lines }) + ya.mgr_emit("arrow", { -lines }) elseif direction == "j" then - ya.manager_emit("arrow", { lines }) + ya.mgr_emit("arrow", { lines }) else - ya.manager_emit("arrow", { lines - 1 }) + ya.mgr_emit("arrow", { lines - 1 }) end - ya.manager_emit("escape", {}) + ya.mgr_emit("escape", {}) if cmd == "d" then - ya.manager_emit("remove", {}) + ya.mgr_emit("remove", {}) elseif cmd == "y" then - ya.manager_emit("yank", {}) + ya.mgr_emit("yank", {}) elseif cmd == "x" then - ya.manager_emit("yank", { cut = true }) + ya.mgr_emit("yank", { cut = true }) end end diff --git a/config/yazi/plugins/yatline.yazi/main.lua b/config/yazi/plugins/yatline.yazi/main.lua index b3af70a2..48591c58 100644 --- a/config/yazi/plugins/yatline.yazi/main.lua +++ b/config/yazi/plugins/yatline.yazi/main.lua @@ -448,7 +448,7 @@ function Yatline.string.get:hovered_file_extension(show_icon) if cha.is_dir then name = "dir" else - name = get_file_extension(hovered.url:name()) + name = get_file_extension(hovered.url.name) end if show_icon then @@ -474,7 +474,7 @@ function Yatline.string.get:tab_path(config) local cwd = cx.active.current.cwd local filter = cx.active.current.files.filter - local search = cwd.is_search and string.format(" (search: %s", cwd:frag()) or "" + local search = cwd.is_search and string.format(" (search: %s", cwd.frag) or "" local suffix if not filter then @@ -593,7 +593,7 @@ function Yatline.line.get:tabs(side) for i = 1, tabs do local text = i if tab_width > 2 then - text = ya.truncate(text .. " " .. cx.tabs[i]:name(), { max = tab_width }) + text = ya.truncate(text .. " " .. cx.tabs[i].name, { max = tab_width }) end separator_style = { bg = nil, fg = nil } @@ -1105,6 +1105,10 @@ return { right = { section_a = {}, section_b = {}, section_c = {} }, } + config.theme = (not rt.term.light and config.theme_dark) + or (rt.term.light and config.theme_light) + or config.theme + if config.theme then for key, value in pairs(config.theme) do if not config[key] then diff --git a/config/zsh/.zshrc b/config/zsh/.zshrc index 3b61082f..ea9865b6 100644 --- a/config/zsh/.zshrc +++ b/config/zsh/.zshrc @@ -165,3 +165,5 @@ zstyle ':fzf-tab:complete:cd:*' fzf-preview 'eza -1 --color=always $realpath' [ -f "$XDG_CONFIG_HOME/zsh/zoxide" ] && source "$XDG_CONFIG_HOME/zsh/zoxide" if [[ ":$FPATH:" != *":$XDG_CONFIG_HOME/zsh/completions:"* ]]; then export FPATH="$XDG_CONFIG_HOME/zsh/completions:$FPATH"; fi + +. "$HOME/.local/share/../bin/env"