From 5237f0f99bda30d7330e8033f0b733f266f2580d Mon Sep 17 00:00:00 2001 From: Daniel Fichtinger Date: Wed, 12 Feb 2025 12:15:06 -0500 Subject: [PATCH] AutoYADM commit: 2025-02-12 12:15:06 --- .config/yazi/init.lua | 4 + .config/yazi/package.toml | 5 + .../yazi/plugins/custom-shell.yazi/LICENSE | 21 ++ .../yazi/plugins/custom-shell.yazi/README.md | 213 +++++++++++++++++ .../yazi/plugins/custom-shell.yazi/main.lua | 223 ++++++++++++++++++ 5 files changed, 466 insertions(+) create mode 100644 .config/yazi/init.lua create mode 100644 .config/yazi/plugins/custom-shell.yazi/LICENSE create mode 100644 .config/yazi/plugins/custom-shell.yazi/README.md create mode 100644 .config/yazi/plugins/custom-shell.yazi/main.lua diff --git a/.config/yazi/init.lua b/.config/yazi/init.lua new file mode 100644 index 00000000..ec388663 --- /dev/null +++ b/.config/yazi/init.lua @@ -0,0 +1,4 @@ +require("custom-shell"):setup({ + history_path = "default", + save_history = true, +}) diff --git a/.config/yazi/package.toml b/.config/yazi/package.toml index 3bff43db..71ef92b1 100644 --- a/.config/yazi/package.toml +++ b/.config/yazi/package.toml @@ -3,6 +3,11 @@ use = "Ape/open-with-cmd" rev = "433cf30" hash = "86e0312b01bc5762813c6c1d0517f073" +[[plugin.deps]] +use = "AnirudhG07/custom-shell" +rev = "6b4550a" +hash = "43019cca17a4d7b41746c2e0a6b8f33f" + [[flavor.deps]] use = "ashen-org/ashen:ashen" rev = "ded1f4e" diff --git a/.config/yazi/plugins/custom-shell.yazi/LICENSE b/.config/yazi/plugins/custom-shell.yazi/LICENSE new file mode 100644 index 00000000..ea3f7795 --- /dev/null +++ b/.config/yazi/plugins/custom-shell.yazi/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Anirudh Gupta + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.config/yazi/plugins/custom-shell.yazi/README.md b/.config/yazi/plugins/custom-shell.yazi/README.md new file mode 100644 index 00000000..4f077941 --- /dev/null +++ b/.config/yazi/plugins/custom-shell.yazi/README.md @@ -0,0 +1,213 @@ +# Custom-shell.yazi + +A yazi plugin to open your custom-shell as well as run your command commands through your yazi Shell, and also save your commands to history. +You can choose any shell and customise keybindings to run any command like nvim, lazygit, cowsay, etc. without inputting the command as well! + +## Previews + +https://github.com/AnirudhG07/custom-shell.yazi/assets/146579014/1cd6ab98-5b79-4ee8-b59a-dbee053edad5 + +## Requirements + +Yazi version 25.2.7 or higher. And of course, your custom-shell as default shell. + +# Installation + +```bash +ya pack -a AnirudhG07/custom-shell + +## For linux and MacOS +git clone https://github.com/AnirudhG07/custom-shell.yazi.git ~/.config/yazi/plugins/custom-shell.yazi + +## For Windows +git clone https://github.com/AnirudhG07/custom-shell.yazi.git %AppData%\yazi\config\plugins\custom-shell.yazi +``` + +Windows user's should check the init.lua file to make sure the paths used are correct. + +# Usage + +## History Setup + +Add the following to your `init.lua` file: + +```lua +require("custom-shell"):setup({ + history_path = "default", + save_history = true, +}) +``` + +The `default` corresponds to `yazi_cmd_history` file in your `~/.config/yazi/plugins/custom-shell.yazi` directory(and similar for Windows). You can specify any other path if you like to save the history file elsewhere. + +The `save_history` option is set to `true` by default, which will enable files to be saved to history. You can disable the behavior by setting it to `false`. + +## Shell Selection + +There are 2 ways you can set your custom-shell. + +- The `auto` mode automatically sets the custom-shell to the value of `$SHELL` environment variable. +- The `shell_name` sets the custom-shell to the shell you want to use. + +| **Mode** | **Description** | +| -------- | ----------------------------------------- | +| `auto` | Automatically set custom-shell = `$SHELL` | +| `zsh` | Set custom-shell = `zsh` | +| `bash` | Set custom-shell = `bash` | +| `fish` | Set custom-shell = `fish` <°))>< | +| `ksh` | Set custom-shell = `ksh` or Kornshell | + +Similarly you can input the name of the shell you want to use. +
+These commands uses the below command to run the shells- + +```bash +custom_shell -ic "command";exit +``` + +You can also set options about the processes to run. The `shell` API for yazi allows the following options: + +1. Interactive: default set to `false`. +2. Block: default set to `false`. +3. Orphan: default set to `false`. + +To change these options, you can give the following arguments to the plugin: + +- To set to `true`, add `--option=true` or simply `--option`. +- To set to `false`, add `--option=false` or simply not add it in the command(unless default is `true`). + +For example: + +- `--block=false` to set block to false. +- `--orphan=true` to set orphan to true. + +Check the keybindings below to see how to set these options. + +You can also add `--wait`(default `false`) to make it wait for the user to press return key after executing the command. This allows the command output not to disappear immediately after exit and to stay readable on screen. Note that it is up to the command you run to decide whether to wait for user input or not, so this option may or may not be needed. + +![wait argument demo](.assets/wait_demo.gif) + +### Keybindings for Custom Shell + +Add this to your `keymap.toml` file: + +To use the `auto` mode, you can set the keymappings as: + +```toml +[[manager.prepend_keymap]] +on = [ "" ] +run = 'plugin custom-shell -- auto --interactive' +desc = "custom-shell as default" +``` + +To choose a specific shell, you can set the keymappings as: + +```toml +[[manager.prepend_keymap]] +on = [ "" ] +run = 'plugin custom-shell zsh' # OR 'plugin custom-shell -- zsh' +desc = "custom-shell as default" +``` + +To set extra shell arguments, you can add them as: + +```toml +[[manager.prepend_keymap]] +on = [ "" ] +run = 'plugin custom-shell -- zsh --interactive --block' +desc = "custom-shell as default with specified arguments" +``` + +To choose a specific shell(or `auto`) and `wait` for user to press return key after executing the command: + +```toml +[[manager.prepend_keymap]] +on = [ "" ] +run = "plugin custom-shell -- zsh --wait" +desc = "custom-shell as default, waits for user" +``` + +You can input any shell with their shortnames or full names like "Powershell" or "pwsh", "nushell" or "nu", "Kornshell" or "ksh", etc. + +### Recommended Keybindings + +```toml +[[manager.prepend_keymap]] +on = [ "'", ";" ] +run = 'plugin custom-shell -- auto --interactive' +desc = "custom-shell as default, interactive" +``` + +```toml +[[manager.prepend_keymap]] +on = [ "'", ":" ] +run = 'plugin custom-shell -- auto --interactive --block' +desc = "custom-shell as default, interactive, block" +``` + +## Custom Commands + +Custom-shell.yazi allows you to run your custom commands without inputting them inside yazi. You can set the shell through which you want to run your command as well. This also supports aliases. + +To run a command, you can set the keymappings as: + +```toml +[[manager.prepend_keymap]] +on = [ "l", "g" ] +run = "plugin custom-shell -- custom auto lazygit" +desc = "Run lazygit" +``` + +You can also run the commands with extra arguments as: + +```toml +[[manager.prepend_keymap]] +on = [ "'", "1" ] +run = "plugin custom-shell -- custom fish 'echo hi' --orphan" +desc = "Run echo hi" +``` + +```toml +[[manager.prepend_keymap]] +on = [ "'", "2" ] +run = "plugin custom-shell -- custom nu 'tmux'" +desc = "Run tmux" +``` + +To make the shell wait for your `ls` command, you can set the keymappings as: + +```toml +[[manager.prepend_keymap]] +on = [ "'", "3" ] +run = "plugin custom-shell -- custom zsh 'ls' --wait" +desc = "Run ls" +``` + +## History + +Custom-shell saves the command you have run in a history file. It uses `fzf` to show history and run the selected command. You can set the keymappings to view the history as - + +```toml +[[manager.prepend_keymap]] +on = [ "'", "h" ] +run = "plugin custom-shell history" +desc = "Show Custom-shell history" +``` + +## Features + +- Open your custom-shell as your default shell like zsh, <°))>< fish, bash, etc. +- Usage of aliases is supported. +- When using 'auto' mode, if you change your default shell, it will automatically change the custom-shell to the new default shell. +- If your shell runs extra commands like printing texts, taskwarrior, newsupdates, etc. when you open the shell, they will not hinder into it's functioning. +- Run custom commands without inputting them inside yazi. +- Set extra arguments for the processes to run. +- Save commands to history and execute them again. + +## 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/). + +## Acknowledgement + +This code is referenced from issue [#1206](https://github.com/sxyazi/yazi/issues/1206) and PR [#84](https://github.com/yazi-rs/yazi-rs.github.io/pull/84) I raised on the repositories. Thank you to the maintainers of sxyazi/yazi for the help. diff --git a/.config/yazi/plugins/custom-shell.yazi/main.lua b/.config/yazi/plugins/custom-shell.yazi/main.lua new file mode 100644 index 00000000..36c259ae --- /dev/null +++ b/.config/yazi/plugins/custom-shell.yazi/main.lua @@ -0,0 +1,223 @@ +local state_option = ya.sync(function(state, attr) + return state[attr] +end) + +local function shell_choice(shell_val) + -- input is in lowercase always + local alt_name_map = { + kornshell = "ksh", + powershell = "pwsh", + nushell = "nu", + } + + local shell_map = { + bash = { shell_val = "bash", supporter = "-ic", wait_cmd = "read", separator = ";" }, + zsh = { shell_val = "zsh", supporter = "-ic", wait_cmd = "read", separator = ";" }, + fish = { shell_val = "fish", supporter = "-c", wait_cmd = "read", separator = ";" }, + pwsh = { shell_val = "pwsh", supporter = "-Command", wait_cmd = "Read-Host", separator = ";" }, + sh = { shell_val = "sh", supporter = "-c", wait_cmd = "read", separator = ";" }, + ksh = { shell_val = "ksh", supporter = "-c", wait_cmd = "read", separator = ";" }, + csh = { shell_val = "csh", supporter = "-c", wait_cmd = "$<", separator = ";" }, + tcsh = { shell_val = "tcsh", supporter = "-c", wait_cmd = "$<", separator = ";" }, + dash = { shell_val = "dash", supporter = "-c", wait_cmd = "read", separator = ";" }, + nu = { shell_val = "nu", supporter = "-l -i -c", wait_cmd = "input", separator = "| print;" }, + } + + shell_val = alt_name_map[shell_val] or shell_val + local shell_info = shell_map[shell_val] + + if shell_info then + return shell_info.shell_val, shell_info.supporter, shell_info.wait_cmd, shell_info.separator + else + return nil, "-c", "read" + end +end + +local function manage_extra_args(job) + -- function for dealing with --option, --option=boolean, nil + local function tobool(arg, default) + if type(arg) == "boolean" then + return arg + elseif type(arg) == "string" then + return arg:lower() == "true" + end + -- Fallback in case of nil + return default + end + + local block = tobool(job.args.block, true) + local orphan = tobool(job.args.orphan, false) + local wait = tobool(job.args.wait, false) + local interactive = tobool(job.args.confirm, false) + + return block, orphan, wait, interactive +end + +local function manage_additional_title_text(block, wait) + local txt = "" + if block or wait then + txt = "(" .. (block and wait and "block and wait" or block and "block" or "") .. ")" + end + return txt +end + +local function history_add(history_path, cmd_run) + local history_file = history_path + local history = {} + + -- Ensure the history file exists + local file = io.open(history_file, "a+") + if file then + file:close() + end + + -- Read existing history + for line in io.lines(history_file) do + -- add line if it is not empty + if line:match("%S") then + table.insert(history, line) + end + end + + if cmd_run == nil then + return history + end + + -- Append the new command to the history if it doesn't already exist + local command_exists = false + for _, cmd in ipairs(history) do + if cmd == cmd_run then + command_exists = true + break + end + end + + if not command_exists then + file = io.open(history_file, "a") + if file then + file:write(cmd_run .. "\n") + file:close() + end + end + + return history +end + +local function history_prev(history_path) + -- get the history commands + local history_cmds = history_add(history_path) + if #history_cmds < 1 then + ya.notify({ title = "Custom-Shell", content = "History is Empty.", timeout = 3 }) + return + end + -- preview the commands list in fzf and return selected cmd + for i, cmd in ipairs(history_cmds) do + history_cmds[i] = cmd:gsub("'", "'\\''") -- Escape single quotes + end + local permit = ya.hide() + + local cmd = string.format('%s < "%s"', "fzf", history_path) + local handle = io.popen(cmd, "r") + + local his_cmd = "" + if handle then + -- strip + his_cmd = string.gsub(handle:read("*all") or "", "^%s*(.-)%s*$", "%1") + handle:close() + end + + permit:drop() + return his_cmd +end + +local function entry(_, job) + local shell_env = os.getenv("SHELL"):match(".*/(.*)") + local shell_value, cmd, custom_shell_cmd = "", "", "" + + local history_path, save_history = state_option("history_path"), state_option("save_history") + + if job.args[1] == "auto" or job.args[1] == "history" then + shell_value = shell_env:lower() + elseif job.args[1] == "custom" then + shell_value = job.args[2] + cmd = job.args[3] + -- when the first param is a shell name + elseif job.args[1] ~= "history" then + shell_value = job.args[1]:lower() + end + + local shell_val, supp, wait_cmd, separator = shell_choice(shell_value:lower()) + + if job.args[1] == "history" then + local his_cmd = history_prev(history_path) + if his_cmd == nil then + return + end + local value, event = ya.input({ + title = "Custom-Shell History", + position = { "top-center", y = 3, w = 40 }, + value = his_cmd, + }) + if event == 1 then + -- this may lead to nested zsh -c "zsh -c 'zsh -c ...'" + -- But that's not a problem + cmd = value + end + end + if shell_val == nil then + ya.notify("Unsupported shell: " .. shell_value .. "Choosing Default Shell: " .. shell_env) + shell_val, supp = shell_choice(shell_env) + end + + local block, orphan, wait, interactive = manage_extra_args(job) -- , confirm + local additional_title_text = manage_additional_title_text(block, wait) + local input_title = shell_value .. " Shell " .. additional_title_text .. ": " + local event = 1 + + if job.args[1] ~= "custom" and job.args[1] ~= "history" then + cmd, event = ya.input({ + title = input_title, + position = { "top-center", y = 3, w = 40 }, + }) + end + + if event == 1 then + local after_cmd = separator .. (wait and wait_cmd or "exit") + -- for history also, this will be added. + custom_shell_cmd = shell_val .. " " .. supp .. " " .. ya.quote(cmd .. after_cmd, true) + + ya.manager_emit("shell", { + custom_shell_cmd, + block = block, + interactive = interactive, + orphan = orphan, + }) + + if save_history then + if job.args[1] == "history" then + -- to avoid nested "zsh -c 'zsh -c ...'" + history_add(history_path, cmd) + else + history_add(history_path, custom_shell_cmd) + end + end + end +end + +--- @since 25.2.7 + +return { + setup = function(state, options) + local default_history_path = ( + ya.target_family() == "windows" + and os.getenv("APPDATA") .. "\\yazi\\config\\plugins\\custom-shell.yazi\\yazi_cmd_history" + ) or (os.getenv("HOME") .. "/.config/yazi/plugins/custom-shell.yazi/yazi_cmd_history") + + state.history_path = options.history_path or default_history_path + if state.history_path:lower() == "default" then + state.history_path = default_history_path + end + state.save_history = options.save_history and true + end, + entry = entry, +}