feat: yazi, clean wezterm

This commit is contained in:
2025-04-12 15:38:25 +02:00
parent ab69b02641
commit 72e7700efb
30 changed files with 4361 additions and 28 deletions

View File

@ -0,0 +1,19 @@
Copyright (c) 2024 dedukun
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.

View File

@ -0,0 +1,146 @@
# bookmarks.yazi
A [Yazi](https://github.com/sxyazi/yazi) plugin that adds the basic functionality of [vi-like marks](https://neovim.io/doc/user/motion.html#mark-motions).
https://github.com/dedukun/bookmarks.yazi/assets/25795432/9a9fe345-dd06-442e-99f1-8475ab22fad5
## Requirements
- [Yazi](https://github.com/sxyazi/yazi) v25.2.7+
## Features
- Create/delete bookmarks
- Custom Notifications
- `''` to go back to the previous folder
- Bookmarks persistence
## Installation
```sh
ya pack -a dedukun/bookmarks
```
## Import/Export bookmarks
This plugin uses [Yazi's DDS](https://yazi-rs.github.io/docs/dds/) for bookmark persistence, as such,
the bookmarks are saved in DDS's state file (`~/.local/state/yazi/.dds` on Linux and `C:\Users\USERNAME\AppData\Roaming\yazi\state\.dds` on Windows)
**_NOTE:_** This system may be used by other plugins that you have installed, so this file might have more data than just the bookmarks.
## Configuration
Add this to your `keymap.toml`:
```toml
[[manager.prepend_keymap]]
on = [ "m" ]
run = "plugin bookmarks save"
desc = "Save current position as a bookmark"
[[manager.prepend_keymap]]
on = [ "'" ]
run = "plugin bookmarks jump"
desc = "Jump to a bookmark"
[[manager.prepend_keymap]]
on = [ "b", "d" ]
run = "plugin bookmarks delete"
desc = "Delete a bookmark"
[[manager.prepend_keymap]]
on = [ "b", "D" ]
run = "plugin bookmarks delete_all"
desc = "Delete all bookmarks"
```
---
Additionally there are configurations that can be done using the plugin's `setup` function in Yazi's `init.lua`, i.e. `~/.config/yazi/init.lua`.
The following are the default configurations:
```lua
-- ~/.config/yazi/init.lua
require("bookmarks"):setup({
last_directory = { enable = false, persist = false, mode="dir" },
persist = "none",
desc_format = "full",
file_pick_mode = "hover",
custom_desc_input = false,
notify = {
enable = false,
timeout = 1,
message = {
new = "New bookmark '<key>' -> '<folder>'",
delete = "Deleted bookmark in '<key>'",
delete_all = "Deleted all bookmarks",
},
},
})
```
### `last_directory`
When enabled, a new bookmark is automatically created in `'` which allows the user to jump back to
the last directory.
There's also the option to enable persistence to this automatic bookmark.
Finally, there's a `mode` option with the following options:
| Value | Description |
| ------ | ------------------------------------------------------------ |
| `jump` | It saves the position before the last used mark |
| `mark` | It saves the last created mark |
| `dir` | Default, it saves the last visited directory (old behaviour) |
### `persist`
When enabled the bookmarks will persist, i.e. if you close and reopen Yazi they will still be
present.
There are three possible values for this option:
| Value | Description |
| ------ | -------------------------------------------------------------------------------------------------------------------- |
| `none` | The default value, i.e., no persistance |
| `all` | All the bookmarks are saved in persistent memory |
| `vim` | This mode emulates the vim global marks, i.e., only the bookmarks in upper case (A-Z) are saved to persistent memory |
### `desc_format`
The format for the bookmark description.
There are two possible values for this option:
| Value | Description |
| -------- | ----------------------------------------------------------------------------------------------- |
| `full` | The default, it shows the full path of the bookmark, i.e., the parent folder + the hovered file |
| `parent` | Only shows the parent folder of the bookmark |
### `file_pick_mode`
The mode for choosing which directory to bookmark.
There are two possible values for this option:
| Value | Description |
| -------- | ------------------------------------------------------------------- |
| `hover` | The default, it uses the path of the hovered file for new bookmarks |
| `parent` | Uses the path of the parent folder for new bookmarks |
### `notify`
When enabled, notifications will be shown when the user creates a new bookmark and deletes one or
all saved bookmarks.
By default the notification has a 1 second timeout that can be changed with `notify.timeout`.
Furthermore, you can customize the notification messages with `notify.message`.
For the `new` and `delete` messages, the `<key>` and `<folder>` keywords can be used, which will be replaced by the respective new/deleted bookmark's associated key and folder.
### `custom_desc_input`
When enabled, user can change description for new bookmark before it is saved.
By default the custom description input is filled with path.

View File

@ -0,0 +1,383 @@
--- @since 25.2.7
-- stylua: ignore
local SUPPORTED_KEYS = {
{ on = "0"}, { on = "1"}, { on = "2"}, { on = "3"}, { on = "4"},
{ on = "5"}, { on = "6"}, { on = "7"}, { on = "8"}, { on = "9"},
{ on = "A"}, { on = "B"}, { on = "C"}, { on = "D"}, { on = "E"},
{ on = "F"}, { on = "G"}, { on = "H"}, { on = "I"}, { on = "J"},
{ on = "K"}, { on = "L"}, { on = "M"}, { on = "N"}, { on = "O"},
{ on = "P"}, { on = "Q"}, { on = "R"}, { on = "S"}, { on = "T"},
{ on = "U"}, { on = "V"}, { on = "W"}, { on = "X"}, { on = "Y"}, { on = "Z"},
{ on = "a"}, { on = "b"}, { on = "c"}, { on = "d"}, { on = "e"},
{ on = "f"}, { on = "g"}, { on = "h"}, { on = "i"}, { on = "j"},
{ on = "k"}, { on = "l"}, { on = "m"}, { on = "n"}, { on = "o"},
{ on = "p"}, { on = "q"}, { on = "r"}, { on = "s"}, { on = "t"},
{ on = "u"}, { on = "v"}, { on = "w"}, { on = "x"}, { on = "y"}, { on = "z"},
}
local _send_notification = ya.sync(
function(state, message)
ya.notify {
title = "Bookmarks",
content = message,
timeout = state.notify.timeout,
}
end
)
local _get_real_index = ya.sync(function(state, idx)
for key, value in pairs(state.bookmarks) do
if value.on == SUPPORTED_KEYS[idx].on then
return key
end
end
return nil
end)
local _get_bookmark_file = ya.sync(function(state)
local folder = cx.active.current
if state.file_pick_mode == "parent" or not folder.hovered then
return { url = folder.cwd, is_parent = true }
end
return { url = folder.hovered.url, is_parent = false }
end)
local _generate_description = ya.sync(function(state, file)
-- if this is true, we don't have information about the folder, so just return the folder url
if file.is_parent then
return tostring(file.url)
end
if state.desc_format == "parent" then
return tostring(file.url:parent())
end
-- full description
return tostring(file.url)
end)
local _load_state = ya.sync(function(state)
ps.sub_remote("@bookmarks", function(body)
if not state.bookmarks and body then
state.bookmarks = {}
for _, value in pairs(body) do
table.insert(state.bookmarks, value)
end
end
end)
end)
local _save_state = ya.sync(function(state, bookmarks)
if not bookmarks then
ps.pub_to(0, "@bookmarks", nil)
return
end
local save_state = {}
if state.persist == "all" then
save_state = bookmarks
else -- VIM mode
local idx = 1
for _, value in pairs(bookmarks) do
-- Only save bookmarks in upper case keys
if string.match(value.on, "%u") then
save_state[idx] = value
idx = idx + 1
end
end
end
ps.pub_to(0, "@bookmarks", save_state)
end)
local _load_last = ya.sync(function(state)
ps.sub_remote("@bookmarks-last", function(body)
state.last_dir = body
if state.last_mode ~= "dir" then
ps.unsub_remote("@bookmarks-last")
end
end)
end)
local _save_last = ya.sync(function(state, persist, imediate)
local file = _get_bookmark_file()
local curr = {
on = "'",
desc = _generate_description(file),
path = tostring(file.url),
is_parent = file.is_parent,
}
if imediate then
state.curr_dir = nil
state.last_dir = curr
else
state.last_dir = state.curr_dir
state.curr_dir = curr
end
if persist and state.last_dir then
ps.pub_to(0, "@bookmarks-last", state.last_dir)
end
end)
local get_last_mode = ya.sync(function(state) return state.last_mode end)
local save_last_dir = ya.sync(function(state)
ps.sub("cd", function() _save_last(state.last_persist, false) end)
ps.sub("hover", function()
local file = _get_bookmark_file()
state.curr_dir.desc = _generate_description(file)
state.curr_dir.path = tostring(file.url)
end)
end)
local save_last_jump = ya.sync(function(state) _save_last(state.last_persist, true) end)
local save_last_mark = ya.sync(function(state) _save_last(state.last_persist, true) end)
local _is_custom_desc_input_enabled = ya.sync(function(state) return state.custom_desc_input end)
-- ***********************************************
-- **============= C O M M A N D S =============**
-- ***********************************************
local save_bookmark = ya.sync(function(state, idx, custom_desc)
local file = _get_bookmark_file()
state.bookmarks = state.bookmarks or {}
local _idx = _get_real_index(idx)
if not _idx then
_idx = #state.bookmarks + 1
end
local bookmark_desc = tostring(file.url)
if custom_desc then
bookmark_desc = tostring(custom_desc)
end
state.bookmarks[_idx] = {
on = SUPPORTED_KEYS[idx].on,
desc = bookmark_desc,
path = tostring(file.url),
is_parent = file.is_parent,
}
-- Custom sorting function
table.sort(state.bookmarks, function(a, b)
local key_a, key_b = a.on, b.on
-- Numbers first
if key_a:match("%d") and not key_b:match("%d") then
return true
elseif key_b:match("%d") and not key_a:match("%d") then
return false
end
-- Uppercase before lowercase
if key_a:match("%u") and key_b:match("%l") then
return true
elseif key_b:match("%u") and key_a:match("%l") then
return false
end
-- Regular alphabetical sorting
return key_a < key_b
end)
if state.persist then
_save_state(state.bookmarks)
end
if state.notify and state.notify.enable then
local message = state.notify.message.new
message, _ = message:gsub("<key>", state.bookmarks[_idx].on)
message, _ = message:gsub("<folder>", state.bookmarks[_idx].desc)
_send_notification(message)
end
if get_last_mode() == "mark" then
save_last_mark()
end
end)
local all_bookmarks = ya.sync(function(state, append_last_dir)
local bookmarks = {}
if state.bookmarks then
for _, value in pairs(state.bookmarks) do
table.insert(bookmarks, value)
end
end
if append_last_dir and state.last_dir then
table.insert(bookmarks, state.last_dir)
end
return bookmarks
end)
local delete_bookmark = ya.sync(function(state, idx)
if state.notify and state.notify.enable then
local message = state.notify.message.delete
message, _ = message:gsub("<key>", state.bookmarks[idx].on)
message, _ = message:gsub("<folder>", state.bookmarks[idx].desc)
_send_notification(message)
end
table.remove(state.bookmarks, idx)
if state.persist then
_save_state(state.bookmarks)
end
end)
local delete_all_bookmarks = ya.sync(function(state)
state.bookmarks = nil
if state.persist then
_save_state(nil)
end
if state.notify and state.notify.enable then
_send_notification(state.notify.message.delete_all)
end
end)
return {
entry = function(_, job)
local action = job.args[1]
if not action then
return
end
if action == "save" then
local key = ya.which { cands = SUPPORTED_KEYS, silent = true }
if key then
if _is_custom_desc_input_enabled() then
local value, event = ya.input {
title = "Save with custom description:",
position = { "top-center", y = 3, w = 60 },
value = tostring(_get_bookmark_file().url),
}
if event ~= 1 then
return
end
save_bookmark(key, value)
return
end
save_bookmark(key)
end
return
end
if action == "delete_all" then
return delete_all_bookmarks()
end
local bookmarks = all_bookmarks(action == "jump")
local selected = #bookmarks > 0 and ya.which { cands = bookmarks }
if not selected then
return
end
if action == "jump" then
if get_last_mode() == "jump" then
save_last_jump()
end
if bookmarks[selected].is_parent then
ya.manager_emit("cd", { bookmarks[selected].path })
else
ya.manager_emit("reveal", { bookmarks[selected].path })
end
elseif action == "delete" then
delete_bookmark(selected)
end
end,
setup = function(state, args)
if not args then
return
end
if type(args.last_directory) == "table" then
if args.last_directory.enable then
if args.last_directory.mode == "mark" then
state.last_persist = args.last_directory.persist
state.last_mode = "mark"
elseif args.last_directory.mode == "jump" then
state.last_persist = args.last_directory.persist
state.last_mode = "jump"
elseif args.last_directory.mode == "dir" then
state.last_persist = args.last_directory.persist
state.last_mode = "dir"
save_last_dir()
else
-- default
state.last_persist = args.last_directory.persist
state.last_mode = "dir"
save_last_dir()
end
if args.last_directory.persist then
_load_last()
end
end
end
if args.persist == "all" or args.persist == "vim" then
state.persist = args.persist
_load_state()
end
if args.desc_format == "parent" then
state.desc_format = "parent"
else
state.desc_format = "full"
end
if args.file_pick_mode == "parent" then
state.file_pick_mode = "parent"
else
state.file_pick_mode = "hover"
end
if type(args.custom_desc_input) == "boolean" then
state.custom_desc_input = args.custom_desc_input
end
state.notify = {
enable = false,
timeout = 1,
message = {
new = "New bookmark '<key>' -> '<folder>'",
delete = "Deleted bookmark in '<key>'",
delete_all = "Deleted all bookmarks",
},
}
if type(args.notify) == "table" then
if type(args.notify.enable) == "boolean" then
state.notify.enable = args.notify.enable
end
if type(args.notify.timeout) == "number" then
state.notify.timeout = args.notify.timeout
end
if type(args.notify.message) == "table" then
if type(args.notify.message.new) == "string" then
state.notify.message.new = args.notify.message.new
end
if type(args.notify.message.delete) == "string" then
state.notify.message.delete = args.notify.message.delete
end
if type(args.notify.message.delete_all) == "string" then
state.notify.message.delete_all = args.notify.message.delete_all
end
end
end
end,
}