diff --git a/.config/nvim/lazy-lock.json b/.config/nvim/lazy-lock.json index 2d01828..9086769 100644 --- a/.config/nvim/lazy-lock.json +++ b/.config/nvim/lazy-lock.json @@ -3,7 +3,9 @@ "avante.nvim": { "branch": "main", "commit": "b57b7d8e601ec6c7675c67de40cc7f5f0c0d40d0" }, "blink.cmp": { "branch": "main", "commit": "78336bc89ee5365633bcf754d93df01678b5c08f" }, "bufferline.nvim": { "branch": "main", "commit": "655133c3b4c3e5e05ec549b9f8cc2894ac6f51b3" }, - "catppuccin": { "branch": "main", "commit": "c7c692a0ad3080710893abbae100171819a3e4be" }, + "catppuccin": { "branch": "main", "commit": "79e2049a0fdf7ec840f8463fe1d962c94493196b" }, + "clangd-container.nvim": { "branch": "master", "commit": "8bfb4f202ea64710ce46d8790ca95c9ee89b6ce6" }, + "clangd_extensions.nvim": { "branch": "main", "commit": "78c2ecd659d54972be17aa6ba2deac3c53223b80" }, "conform.nvim": { "branch": "master", "commit": "619363c30309d29ffa631e67c8183f2a72caa373" }, "diffview.nvim": { "branch": "main", "commit": "4516612fe98ff56ae0415a259ff6361a89419b0a" }, "dotenv.nvim": { "branch": "main", "commit": "7d516e9293c6e3ac21830fb10a4e8674c02747c6" }, @@ -26,14 +28,16 @@ "mini.pairs": { "branch": "main", "commit": "b1c5a726921b7a8c9321e9a7a208aa0571de5810" }, "noice.nvim": { "branch": "main", "commit": "7bfd942445fb63089b59f97ca487d605e715f155" }, "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" }, + "nvim-dev-container": { "branch": "main", "commit": "365b4c815339d42133829006dce3e5f8b182b771" }, "nvim-lint": { "branch": "master", "commit": "a219b2c9e5b4765e5c845aba119dad55806fcaf1" }, - "nvim-lspconfig": { "branch": "master", "commit": "d592c1e6ad9a0a01b3d5ed3f0345d68407167181" }, - "nvim-treesitter": { "branch": "main", "commit": "a03c6283b079df3ec55705d4702d515ab2072267" }, + "nvim-lspconfig": { "branch": "master", "commit": "7ab79bb1b57160a537f427ac8a6026102f12b701" }, + "nvim-treesitter": { "branch": "main", "commit": "61df84986b4b4ec469ee745a182e433d49f8c27e" }, "nvim-treesitter-textobjects": { "branch": "main", "commit": "898ee307df58f854d11cd7edd06472574d48014e" }, "nvim-ts-autotag": { "branch": "main", "commit": "88c1453db4ba7dd24131086fe51fdf74e587d275" }, "persistence.nvim": { "branch": "main", "commit": "b20b2a7887bd39c1a356980b45e03250f3dce49c" }, "plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" }, "render-markdown.nvim": { "branch": "main", "commit": "f422cb5c6855f150e2ddcfaf44e7157b98b34f6a" }, + "snacks-terminal-ctl.nvim": { "branch": "master", "commit": "97727421c8d0ffa33d893f22e38cda93e9010742" }, "snacks.nvim": { "branch": "main", "commit": "882c996cf28183f4d63640de0b4c02ec886d01f2" }, "todo-comments.nvim": { "branch": "main", "commit": "31e3c38ce9b29781e4422fc0322eb0a21f4e8668" }, "tokyonight.nvim": { "branch": "main", "commit": "cdc07ac78467a233fd62c493de29a17e0cf2b2b6" }, diff --git a/.config/nvim/lua/config/keymaps.lua b/.config/nvim/lua/config/keymaps.lua index ee0d997..cdb105e 100644 --- a/.config/nvim/lua/config/keymaps.lua +++ b/.config/nvim/lua/config/keymaps.lua @@ -2,16 +2,5 @@ -- Default keymaps that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/keymaps.lua -- Add any additional keymaps here --- Override LazyVim's / : count 99 opens the floating terminal, --- everything else stays native (bottom terminal at project root). --- Both keys are mapped because terminals differ in which code Ctrl+/ sends. -local function term_open() - if vim.v.count == 99 then - require("config.terminal").open_float() - else - Snacks.terminal.focus(nil, { cwd = LazyVim.root() }) - end -end - -vim.keymap.set({ "n", "t" }, "", term_open, { desc = "Terminal (Root Dir) | 99 = float" }) -vim.keymap.set({ "n", "t" }, "", term_open, { desc = "Terminal (Root Dir) | 99 = float" }) +-- Terminal keymaps ( toggle with 99-count float, tt, ft) +-- are registered by the snacks-terminal-ctl plugin (lua/plugins/terminal.lua). diff --git a/.config/nvim/lua/config/terminal.lua b/.config/nvim/lua/config/terminal.lua deleted file mode 100644 index 1a27d4f..0000000 --- a/.config/nvim/lua/config/terminal.lua +++ /dev/null @@ -1,145 +0,0 @@ --- Terminal helpers layered on top of the native LazyVim/Snacks terminal. --- --- Native behaviour is kept intact and untouched: --- / toggle a bottom terminal at the project root (supports a --- count, e.g. 2 opens a *separate* terminal #2) --- fT terminal in the current working directory --- --- This module adds (actual key bindings are declared in lua/plugins/terminal.lua): --- open_float() open/focus a *dedicated* floating terminal (close it with ) --- picker() list every live terminal so any contextual terminal can be --- focused or restored even after its window was closed/hidden. -local M = {} - --- The floating terminal. Tracked as an upvalue so the same key always re-focuses --- the *same* float regardless of which directory the editor is in. ----@type snacks.win? -local float - ---- Open or focus the floating terminal, or hide it if already focused. --- NOTE: currently unused; the binding calls open_float() instead so shell --- tab-completion keeps working while the terminal is open. Kept around for a --- future explicit open/close toggle. -function M.toggle_float() - -- Reuse the existing instance: hide if visible, re-show if hidden. - if float and float:buf_valid() then - if float:win_valid() then - float:hide() - else - float:show() - vim.cmd("startinsert") - end - return - end - - -- First open: a high fixed count guarantees this terminal never collides with - -- the count-keyed bottom terminals (1, 2, 3 ...), so it is always the *same* - -- float for no matter the context. - float = Snacks.terminal(nil, { - count = 99, - win = { position = "float", border = "rounded" }, - }) - - -- Close it with a single keystroke from inside the shell: hides the - -- window (buffer-local, so it only affects this floating terminal and never - -- the bottom split, where shell tab-completion must keep working). - if float and float.buf then - vim.keymap.set("t", "tt", function() - if float and float:win_valid() then - float:hide() - end - end, { buffer = float.buf, silent = true, desc = "Hide floating terminal" }) - end -end - ---- Open or focus the floating terminal. This never hides the window, so ---- once the float is open the key falls through to the shell and stays available ---- for tab-completion. Close the floating terminal with while it is ---- visible (handled by Snacks' native terminal keymap). -function M.open_float() - if float and float:buf_valid() then - if not float:win_valid() then - float:show() - end - if float:win_valid() then - vim.api.nvim_set_current_win(float.win) - end - vim.cmd("startinsert") - return - end - - -- First open: a high fixed count guarantees this terminal never collides with - -- the count-keyed bottom terminals (1, 2, 3 ...), so it is always the *same* - -- float for no matter the context. - float = Snacks.terminal(nil, { - count = 99, - win = { position = "float", border = "rounded" }, - }) -end - ---- List every live Snacks terminal in a picker. Restore/focus one with , ---- kill one with . -function M.picker() - local terms = Snacks.terminal.list() - if #terms == 0 then - Snacks.notify.info("No open terminals") - return - end - - local items = {} ---@type snacks.picker.finder.Item[] - for _, term in ipairs(terms) do - local meta = vim.b[term.buf] and vim.b[term.buf].snacks_terminal or {} - items[#items + 1] = { - term = term, - buf = term.buf, - id = meta.id, - cwd = meta.cwd or "?", - cmd = meta.cmd or vim.o.shell, - visible = term:win_valid(), - text = ("#%s %s %s"):format(meta.id or "?", meta.cwd or "?", meta.cmd or vim.o.shell), - } - end - - Snacks.picker({ - title = "Terminals", - items = items, - format = function(item) - local ret = {} ---@type snacks.picker.Highlight[] - ret[#ret + 1] = { - item.visible and "● " or "○ ", - item.visible and "SnacksPickerSpecial" or "SnacksPickerComment", - } - ret[#ret + 1] = { ("#%-2s"):format(item.id or "?"), "Number" } - ret[#ret + 1] = { " " .. vim.fn.fnamemodify(item.cwd, ":~"), "SnacksPickerPath" } - ret[#ret + 1] = { " " .. vim.fn.fnamemodify(item.cmd, ":t"), "SnacksPickerComment" } - return ret - end, - confirm = function(picker, item) - picker:close() - local term = item.term - if not term:buf_valid() then - Snacks.notify.warn("Terminal no longer exists") - return - end - if term:win_valid() then - vim.api.nvim_set_current_win(term.win) - else - term:show() - vim.cmd("startinsert") - end - end, - win = { input = { keys = { ["d"] = "term_delete" } } }, - actions = { - term_delete = function(picker, item) - local term = item.term - picker:close() - if term and term:buf_valid() then - term:close() - pcall(vim.api.nvim_buf_delete, term.buf, { force = true }) - end - end, - }, - }) -end - -return M diff --git a/.config/nvim/lua/plugins/clangd-container.lua b/.config/nvim/lua/plugins/clangd-container.lua new file mode 100644 index 0000000..c133fdc --- /dev/null +++ b/.config/nvim/lua/plugins/clangd-container.lua @@ -0,0 +1,11 @@ +return { + { + "chodak166/clangd-container.nvim", + -- dir = vim.fn.expand("~/src/git/clangd-container.nvim"), + dependencies = { "p00f/clangd_extensions.nvim" }, + opts = {}, + keys = { + { "cC", "ClangdContainer", desc = "Clangd Container" }, + }, + }, +} diff --git a/.config/nvim/lua/plugins/devcontainers.lua b/.config/nvim/lua/plugins/devcontainers.lua new file mode 100644 index 0000000..3eedd0a --- /dev/null +++ b/.config/nvim/lua/plugins/devcontainers.lua @@ -0,0 +1,13 @@ +return { + { + "https://codeberg.org/esensar/nvim-dev-container", + dependencies = "nvim-treesitter/nvim-treesitter", + config = function() + require("devcontainer").setup({ + -- Your options go here + -- e.g., auto_start = true, icons = { ... } + container_runtime = docker, + }) + end, + }, +} diff --git a/.config/nvim/lua/plugins/terminal.lua b/.config/nvim/lua/plugins/terminal.lua index 4ac7335..81e5bb7 100644 --- a/.config/nvim/lua/plugins/terminal.lua +++ b/.config/nvim/lua/plugins/terminal.lua @@ -1,23 +1,11 @@ -- Terminal UX layered on Snacks (replaces toggleterm). --- Helpers live in lua/config/terminal.lua; keys are declared here so lazy.nvim --- registers the which-key labels with the rest of the snacks spec. +-- All keymaps (tt, ft, with 99-count float) are +-- registered by the plugin's setup(). return { - "folke/snacks.nvim", - keys = { - { - "tt", - function() - require("config.terminal").open_float() - end, - desc = "Terminal (float)", - mode = "n", - }, - { - "ft", - function() - require("config.terminal").picker() - end, - desc = "Terminals (list/restore)", - }, - }, + "chodak166/snacks-terminal-ctl.nvim", + -- dir = "~/src/git/snacks-terminal-ctl.nvim", + name = "snacks-terminal-ctl.nvim", + dependencies = { "folke/snacks.nvim" }, + event = "VeryLazy", + opts = {}, } diff --git a/.config/yazi/keymap.toml b/.config/yazi/keymap.toml index ae4495f..f957efa 100644 --- a/.config/yazi/keymap.toml +++ b/.config/yazi/keymap.toml @@ -54,3 +54,8 @@ on = "" run = "plugin diff" desc = "Diff the selected with the hovered file" +[[mgr.prepend_keymap]] +on = "p" +run = "plugin paste-dialog" +desc = "Paste with conflict dialog" + diff --git a/.config/yazi/package.toml b/.config/yazi/package.toml index 21f35ee..cc824e9 100644 --- a/.config/yazi/package.toml +++ b/.config/yazi/package.toml @@ -58,6 +58,11 @@ use = "yazi-rs/plugins:jump-to-char" rev = "598cdb6" hash = "7a4b4237223aaa94e589d1c01a23542a" +[[plugin.deps]] +use = "chodak166/paste-dialog" +rev = "645b32a" +hash = "357d322e81ff7449c0249ae17b55aaa5" + [[flavor.deps]] use = "Chromium-3-Oxide/everforest-medium" rev = "e1ead7b" diff --git a/.config/yazi/plugins/paste-dialog.lua b/.config/yazi/plugins/paste-dialog.lua new file mode 100644 index 0000000..987b471 --- /dev/null +++ b/.config/yazi/plugins/paste-dialog.lua @@ -0,0 +1,135 @@ +local M = {} + +local function get_yanked_info() + return ya.sync(function() + local cwd = cx.active.current.cwd + local cut = cx.yanked.is_cut + + local yanked = {} + for _, f in pairs(cx.yanked) do + yanked[#yanked + 1] = { url = f.url, name = tostring(f.url:name()) } + end + + return cwd, cut, yanked + end) +end + +local function rename_default(name) + local suffix = os.date("-%y%m%d") + local stem = name:match("^(.+)%..-$") or name + local ext = name:match("%.(.+)$") or "" + if ext ~= "" then + return stem .. suffix .. "." .. ext + else + return name .. suffix + end +end + +function M:entry(_, job) + local cwd, cut, yanked = get_yanked_info() + if #yanked == 0 then + return + end + + local follow = job.args.follow or false + local resolved = {} + + -- Partition: check each yanked item for conflicts + local conflicts = {} + for _, item in ipairs(yanked) do + local dest = cwd:join(item.name) + local cha = fs.cha(dest) + if cha ~= nil then + conflicts[#conflicts + 1] = { url = item.url, dest = dest, name = item.name } + else + resolved[#resolved + 1] = { + from = tostring(item.url), + to = tostring(dest), + overwrite = false, + } + end + end + + -- Process conflicts + local sync_all = false + for _, item in ipairs(conflicts) do + if sync_all then + resolved[#resolved + 1] = { + from = tostring(item.url), + to = tostring(item.dest), + overwrite = true, + } + else + local choice = ya.pick({ + title = "Paste conflict: " .. item.name, + items = { + "Sync (merge/overwrite)", + "Sync all remaining", + "Rename", + "Skip", + "Cancel", + }, + }) + + -- ya.pick returns 0-based index or nil + if choice == nil or choice == 4 then + -- Cancel: paste what we have so far, then abort + if #resolved > 0 then + ya.emit("paste_resolved", { + cut = cut, + follow = follow, + items = resolved, + }) + end + return + + elseif choice == 0 then + -- Sync current + resolved[#resolved + 1] = { + from = tostring(item.url), + to = tostring(item.dest), + overwrite = true, + } + + elseif choice == 1 then + -- Sync all remaining + sync_all = true + resolved[#resolved + 1] = { + from = tostring(item.url), + to = tostring(item.dest), + overwrite = true, + } + + elseif choice == 2 then + -- Rename + local default = rename_default(item.name) + local value, event = ya.input({ + title = "New name", + value = default, + }) + if event == 1 then + local new_dest = cwd:join(value) + resolved[#resolved + 1] = { + from = tostring(item.url), + to = tostring(new_dest), + overwrite = false, + } + end + + elseif choice == 3 then + -- Skip: do nothing + end + end + end + + -- Dispatch all resolved items + if #resolved > 0 then + ya.emit("paste_resolved", { + cut = cut, + follow = follow, + items = resolved, + }) + end +end + +return M diff --git a/.config/yazi/plugins/paste-dialog.yazi/LICENSE b/.config/yazi/plugins/paste-dialog.yazi/LICENSE new file mode 100644 index 0000000..f360370 --- /dev/null +++ b/.config/yazi/plugins/paste-dialog.yazi/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 chodak166 + +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/paste-dialog.yazi/README.md b/.config/yazi/plugins/paste-dialog.yazi/README.md new file mode 100644 index 0000000..52af933 --- /dev/null +++ b/.config/yazi/plugins/paste-dialog.yazi/README.md @@ -0,0 +1,115 @@ +# paste-dialog.yazi + +A [yazi](https://github.com/sxyazi/yazi) plugin that shows an interactive +conflict-resolution dialog when pasting files. Instead of silently appending +`_1`, `_2` suffixes to conflicting names, you choose how to handle each +conflict individually. + +| Option | Action | Description | +|--------|--------|-------------| +| **Merge** | merge / overwrite | Merge directory contents recursively; overwrite individual files. _(default — press Enter)_ | +| **Merge all remaining** | merge / overwrite | Same as Merge, but applied to every remaining conflict without prompting again. | +| **Replace** | replace | Delete the destination entirely, then copy fresh (removes files that exist only at the destination). | +| **Rename** | rename | Prompt for a new name, pre-filled with `name-yymmdd`. If the new name also conflicts the dialog reappears with a different default (`name-2`, `name-3`, …). | +| **Skip** | skip | Leave this item untouched. | +| **Cancel** | abort | Paste any non-conflicting items already queued, then stop. _(Esc)_ | + +Non-conflicting items always paste silently — the dialog only appears when a +conflict is detected. + +> ⚠️ **This plugin requires a modified version of yazi.** +> See [Requirements](#requirements) below. + +--- + +## Requirements + +This plugin depends on Rust-level changes to yazi that have **not** been merged +into upstream: + +| Change | Why it's needed | +|--------|----------------| +| `paste_resolved` actor + parser | Per-item paste with explicit `from`/`to`/`overwrite`/`replace` — not possible with the existing `ya.emit("paste")`. | +| `file_copy_one` / `file_cut_one` | Scheduler methods that accept individual source/destination URLs with a `replace` flag. | +| `ya.pick({ title=, items= })` | Exposes the existing pick dialog to Lua plugins (was internal-only). | +| `PickCfg::with_title()` | Lets `ya.pick()` override the dialog title. | + +Build yazi from the fork: + +```sh +git clone https://github.com/chodak166/yazi.git +cd yazi +cargo build --release + +# Install the modified binaries on your PATH +cp target/release/yazi target/release/ya ~/.local/bin/ +``` + +--- + +## Installation + +### Option A — install via `ya pkg` (recommended) + +```sh +ya pkg add chodak166/paste-dialog.yazi +``` + +### Option B — manual install + +```sh +# 1. Create the plugin directory +mkdir -p ~/.config/yazi/plugins/paste-dialog.yazi + +# 2. Download main.lua into it +curl -fsSL https://raw.githubusercontent.com/chodak166/paste-dialog.yazi/main/main.lua \ + -o ~/.config/yazi/plugins/paste-dialog.yazi/main.lua +``` + +### Add the keybinding + +Add the following to `~/.config/yazi/keymap.toml` (the `prepend_keymap` keeps +all default bindings intact): + +```toml +[[mgr.prepend_keymap]] +on = "p" +run = "plugin paste-dialog" +desc = "Paste with conflict dialog" +``` + +Restart yazi, then press p to paste with conflict resolution. + +--- + +## Usage + +1. Yank files (y) or cut them (x) as usual. +2. Navigate to the destination directory. +3. Press p to paste. +4. If there are conflicts a pick dialog appears for each one: + - Navigate with j/k or arrow keys, confirm with Enter. + - **Merge** — overwrites the destination file or merges directory contents. + - **Merge all remaining** — merge this and all remaining conflicts without prompting. + - **Replace** — deletes the destination entirely before copying (removes excess files). + - **Rename** — shows an input prompt with `name-yymmdd` pre-filled. If the new name also conflicts, the dialog reappears; choosing Rename again cycles the default to `name-2`, `name-3`, etc. + - **Skip** — leaves the destination untouched. + - **Cancel** (or Esc) — pastes any queued clean items, then aborts. + +--- + +## Notes + +- **Merge** uses the scheduler's existing `force=true` behavior: new files are + added, existing files are overwritten, destination-only files are preserved. +- **Replace** deletes the destination directory entirely before copying, + removing any files that exist only in the destination. +- Deep conflicts within merged directories are resolved silently by + overwriting individual files. +- **Cut + Skip** leaves the source file in place (it is not deleted). +- Between the conflict pre-scan and the actual paste, `overwrite=false` items + fall back to `unique_file()` if a new conflict appears. + +## License + +MIT diff --git a/.config/yazi/plugins/paste-dialog.yazi/main.lua b/.config/yazi/plugins/paste-dialog.yazi/main.lua new file mode 100644 index 0000000..5b25bfd --- /dev/null +++ b/.config/yazi/plugins/paste-dialog.yazi/main.lua @@ -0,0 +1,220 @@ +local M = {} + +local get_yanked_info = ya.sync(function() + local cwd = cx.active.current.cwd + local cut = cx.yanked.is_cut + + local yanked = {} + for _, url in ipairs(cx.yanked:urls()) do + yanked[#yanked + 1] = { url = url, name = tostring(url.name) } + end + + return { cwd = cwd, cut = cut, yanked = yanked } +end) + +local function rename_default(name, attempt) + if attempt and attempt > 1 then + local suffix = string.format("-%d", attempt) + local stem = name:match("^(.+)%..-$") or name + local ext = name:match("%.(.+)$") or "" + if ext ~= "" then + return stem .. suffix .. "." .. ext + else + return name .. suffix + end + end + local suffix = os.date("-%y%m%d") + local stem = name:match("^(.+)%..-$") or name + local ext = name:match("%.(.+)$") or "" + if ext ~= "" then + return stem .. suffix .. "." .. ext + else + return name .. suffix + end +end + +--Choices: +-- 0: Merge — merge directory contents, overwrite individual files +-- 1: Merge all — same, applied to all remaining conflicts +-- 2: Replace — replace destination entirely (delete then copy) +-- 3: Rename — prompt for a new name +-- 4: Skip — skip this item +-- 5: Cancel(nil) — abort +local function pick_conflict(name) + return ya.pick({ + title = "Paste conflict: " .. name, + items = { + "Merge", + "Merge all remaining", + "Replace", + "Rename", + "Skip", + "Cancel", + }, + }) +end + +function M:entry(job) + local info = get_yanked_info() + local cwd, cut, yanked = info.cwd, info.cut, info.yanked + + if #yanked == 0 then + return + end + + local follow = job.args.follow or false + local resolved = {} + + -- Partition: check each yanked item for conflicts + local conflicts = {} + for _, item in ipairs(yanked) do + local dest = cwd:join(item.name) + local cha = fs.cha(dest) + if cha ~= nil then + conflicts[#conflicts + 1] = { url = item.url, dest = dest, name = item.name } + else + resolved[#resolved + 1] = { + from = tostring(item.url), + to = tostring(dest), + overwrite = false, + replace = false, + } + end + end + + -- Dispatch helper + local function dispatch(items) + if #items > 0 then + ya.emit("paste_resolved", { + cut = cut, + follow = follow, + items = items, + }) + end + end + + -- Process conflicts + local merge_all = false + for _, item in ipairs(conflicts) do + if merge_all then + resolved[#resolved + 1] = { + from = tostring(item.url), + to = tostring(item.dest), + overwrite = true, + replace = false, + } + else + local choice = pick_conflict(item.name) + + if choice == nil or choice == 5 then + -- Cancel: paste what we have so far, then abort + dispatch(resolved) + return + + elseif choice == 0 then + -- Merge current + resolved[#resolved + 1] = { + from = tostring(item.url), + to = tostring(item.dest), + overwrite = true, + replace = false, + } + + elseif choice == 1 then + -- Merge all remaining + merge_all = true + resolved[#resolved + 1] = { + from = tostring(item.url), + to = tostring(item.dest), + overwrite = true, + replace = false, + } + + elseif choice == 2 then + -- Replace + resolved[#resolved + 1] = { + from = tostring(item.url), + to = tostring(item.dest), + overwrite = true, + replace = true, + } + + elseif choice == 3 then + -- Rename: loop until a non-conflicting name is chosen or cancelled + local attempt = 0 + while true do + attempt = attempt + 1 + local default = rename_default(item.name, attempt) + local value, event = ya.input({ + title = "New name", + value = default, + pos = { "center", w = 60 }, + }) + if event ~= 1 then + -- User cancelled the input — skip this item + break + end + + local new_dest = cwd:join(value) + if fs.cha(new_dest) == nil then + -- No conflict — add and continue + resolved[#resolved + 1] = { + from = tostring(item.url), + to = tostring(new_dest), + overwrite = false, + replace = false, + } + break + else + -- New name also conflicts — show the conflict dialog again + local sub_choice = pick_conflict(value) + if sub_choice == nil or sub_choice == 5 then + -- Cancel everything + dispatch(resolved) + return + elseif sub_choice == 0 then + resolved[#resolved + 1] = { + from = tostring(item.url), + to = tostring(new_dest), + overwrite = true, + replace = false, + } + break + elseif sub_choice == 1 then + merge_all = true + resolved[#resolved + 1] = { + from = tostring(item.url), + to = tostring(new_dest), + overwrite = true, + replace = false, + } + break + elseif sub_choice == 2 then + resolved[#resolved + 1] = { + from = tostring(item.url), + to = tostring(new_dest), + overwrite = true, + replace = true, + } + break + elseif sub_choice == 3 then + -- Rename again — increment attempt for different default + -- (loop continues) + elseif sub_choice == 4 then + -- Skip + break + end + end + end + + elseif choice == 4 then + -- Skip: do nothing + end + end + end + + -- Dispatch all resolved items + dispatch(resolved) +end + +return M diff --git a/.gitignore b/.gitignore index dc42dd0..6f04d2c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ .config/nvim/.env .local/share/applications/mimeinfo.cache .docker/config.json +.crush + diff --git a/.zshrc b/.zshrc index 6e93f8f..c77e271 100644 --- a/.zshrc +++ b/.zshrc @@ -106,6 +106,10 @@ fi # Initialize modules. source ${ZIM_HOME}/init.zsh +# Override bira prompt: user@host in gray instead of green +PS1='╭─%B%(!.%F{red]}.%F{8})%n@%m %F{blue}%~${(e)git_info[prompt]}${VIRTUAL_ENV:+" %F{green}‹${VIRTUAL_ENV:t}›"}%f%b +╰─%B%(!.#.$)%b ' + # ------------------------------ # Post-init module configuration # ------------------------------ @@ -164,10 +168,11 @@ if [[ "$TERM" == screen* ]]; then fi typeset -U path -local custom_paths=( +custom_paths=( "$HOME/.cargo/bin" "$HOME/.local/kitty.app/bin" "$HOME/.local/bin" + "$HOME/go/bin" ) for d in "${custom_paths[@]}"; do @@ -185,6 +190,7 @@ alias gl="git log --graph --abbrev-commit --decorate --format=format:'%C(bold bl alias git-config-pb='git config user.name "Łukasz Chodyła" && git config user.email "lukasz.chodyla@platformabiznesu.net"' alias git-config-ch='git config user.name "chodak166" && git config user.email "chodak166@op.pl"' alias config='/usr/bin/git --git-dir=$HOME/.dotfiles.git/ --work-tree=$HOME' +alias config-lazygit='lazygit --git-dir=$HOME/.dotfiles.git/ --work-tree=$HOME' alias v='nvim' alias se=sudoedit