diff --git a/custom/meow.el b/custom/meow.el index fbd13ff..a93431c 100644 --- a/custom/meow.el +++ b/custom/meow.el @@ -110,11 +110,11 @@ '("o" . meow-block) '("O" . meow-to-block) '("p" . meow-yank) - '("q" . meow-quit) + ;;'("q" . meow-quit) '("Q" . meow-goto-line) '("r" . meow-replace) '("R" . meow-swap-grab) - '("s" . meow-kill) + '("k" . meow-kill) '("t" . meow-till) '("u" . meow-undo) '("U" . meow-undo-in-selection) @@ -127,3 +127,4 @@ '("Y" . meow-sync-grab) '("z" . meow-pop-selection) '("'" . repeat))) + diff --git a/custom/system.el b/custom/system.el new file mode 100644 index 0000000..e609923 --- /dev/null +++ b/custom/system.el @@ -0,0 +1,53 @@ +;;; system.el --- System integration functions -*- lexical-binding: t; -*- +(defun my/system-cut (beg end) + "Cut region directly to system clipboard, bypassing the kill ring." + (interactive "r") + (my/system-copy beg end) + ;; delete-region deletes without adding to the kill ring + (delete-region beg end)) + +(defun my/system-copy (beg end) + "Pure copy: Sends the active region directly to the system clipboard. +Absolutely zero interaction with the Emacs kill ring." + (interactive "r") + (unless (region-active-p) + (user-error "No active region to copy")) + (let ((text (buffer-substring-no-properties beg end))) + (if (display-graphic-p) + ;; Low-level GUI primitive: talks directly to the OS clipboard manager + (gui-backend-set-selection 'CLIPBOARD text) + ;; TUI Fallback via Clipetty + (if (fboundp 'clipetty-set-selection) + (clipetty-set-selection text) + (error "Clipetty not loaded"))) + (deactivate-mark) + (message "Copied to system clipboard (pure)"))) + +(defun my/system-paste () + "Pure paste: Inserts text directly from the system clipboard. +Never falls back to or checks the Emacs kill ring." + (interactive) + (if (display-graphic-p) + (let ((text (or (gui-backend-get-selection 'CLIPBOARD 'STRING) + (gui-backend-get-selection 'CLIPBOARD 'UTF8_STRING) + (gui-backend-get-selection 'PRIMARY 'STRING) + ;; Wayland/Dolphin fallback from previous fix + (when (executable-find "wl-paste") + (let ((val (shell-command-to-string "wl-paste -n -t text/plain"))) + (unless (string-empty-p val) val)))))) + (if text + (insert text) + (user-error "System clipboard is empty"))) + ;; TUI Fallback + (message "In a terminal, use your terminal emulator's native paste shortcut (e.g., Ctrl-Shift-V)."))) + +(defun my/vterm-sync-here () + "Switch to vterm and instantly 'cd' to the current buffer's directory." + (interactive) + (let ((cwd default-directory)) + (vterm-toggle) + (vterm-send-string (format "cd %s" (shell-quote-argument cwd))) + (vterm-send-return))) + +(provide 'system) +;;unqualified-search-registries = ["docker.io"] diff --git a/custom/ui.el b/custom/ui.el index ecdcacc..7047d05 100644 --- a/custom/ui.el +++ b/custom/ui.el @@ -1,13 +1,27 @@ ;;; ../../lyfi/configs/apps/doom/custom/ui.el -*- lexical-binding: t; -*- - (defun my/apply-theme (frame) - "Apply a specific theme based on whether the FRAME is graphic or TUI." + "Apply a specific theme and update package colors based on whether the FRAME is graphic or TUI." (select-frame frame) (if (display-graphic-p frame) (progn (mapc #'disable-theme custom-enabled-themes) - (load-theme 'cutarv32 t)) ;; Your GUI theme + (load-theme 'cutarv41 t) + ;; Update rainbow-delimiters colors for Light theme + (setq rainbow-delimiters-rainbow-colors + '("#007cbf" "#cf3f29" "#2374de" "#c75e10" "#b05197" "#429e66" "#129cc9" "#a64dd6"))) (progn (mapc #'disable-theme custom-enabled-themes) - (load-theme 'cutarv32_dark t)))) ;; Your TUI theme + (load-theme 'cutarv41 t) + ;; Update rainbow-delimiters colors for Dark theme + ;; (setq rainbow-delimiters-rainbow-colors + ;; '("#4da6ff" "#ff6b6b" "#6da6ff" "#ffb347" "#d18ecb" "#8fbc8f" "#80daeb" "#c8a0f2")) + )) +(custom-set-faces + `(meow-insert-cursor ((t (:inherit unspecified :background ,(face-attribute 'font-lock-builtin-face :foreground))))) + `(meow-normal-cursor ((t (:foreground ,(face-attribute 'mode-line-inactive :background), :background ,(face-attribute 'mode-line :foreground))))) + ) + ) + + +(provide 'ui) diff --git a/custom/utils.el b/custom/utils.el new file mode 100644 index 0000000..5890705 --- /dev/null +++ b/custom/utils.el @@ -0,0 +1,174 @@ +;;; utils.el --- General utility functions -*- lexical-binding: t; -*- + +(defun dired-follow-symlink () + "In dired, visit the file or directory at point by its true physical path." + (interactive) + (let ((filename (dired-get-file-for-visit))) + (if filename + (find-file (file-truename filename)) + (message "No file at point")))) + +(defun my/delete-word-backward () + "Delete the previous 'block' of characters without killing them. +Blocks are defined by: whitespace, alphanumerics, or groups of symbols." + (interactive) + (if (use-region-p) + (delete-region (region-beginning) (region-end)) + (let ((end (point))) + (save-excursion + (cond + ;; Case 1: Multiple spaces or tabs (but not a newline) + ((looking-back "[ \t]+" 1) + (re-search-backward "[^ \t]" nil t) + (forward-char 1)) + + ;; Case 2: Newlines + ((looking-back "\n+" 1) + (re-search-backward "[^\n]" nil t) + (forward-char 1)) + + ;; Case 3: Alphanumerics (Words) + ((looking-back "[[:alnum:]]+" 1) + (re-search-backward "[^[:alnum:]]" nil t) + (forward-char 1)) + + ;; Case 4: Special Characters (Symbols) + (t + (re-search-backward "[[:alnum:][:space:]]" nil t) + (forward-char 1))) + + (delete-region (point) end))))) + +(defun my-set-buffer-font () + "Set a specific font for the current buffer only." + (buffer-face-set '(:family "Noto Sans" :height 140))) + +(defun my/toggle-quote-wrap-all-in-region (beg end) + "Toggle wrapping all items in region with double quotes." + ;;https://xenodium.com/emacs-quote-wrap-all-in-region + (interactive (list (mark) (point))) + (unless (region-active-p) + (user-error "no region to wrap")) + (let ((deactivate-mark nil) + (replacement (string-join + (mapcar (lambda (item) + (if (string-match-p "^\".*\"$" item) + (string-trim item "\"" "\"") + (format "\"%s\"" item))) + (split-string (buffer-substring beg end))) + " "))) + (delete-region beg end) + (insert replacement))) + +(defun my/meow-wrap-arbitrary (s e) + "Wrap the current meow selection with arbitrary strings." + (interactive "r") + (let ((start-str (read-string "Start: ")) + (end-str (read-string "End: "))) + (save-excursion + (goto-char e) + (insert end-str) + (goto-char s) + (insert start-str)))) + +(defun my/column-align-regexp () + "Aligns the matched regexp to `comment-column`. +If the text preceding the regexp is already past `comment-column`, +it ensures exactly one space of separation." + (interactive) + (let* ((regexp (read-string "Align on regexp: ")) + ;; Use comment-column if set, otherwise default to 40 + (target-col (if (and (boundp 'comment-column) + (numberp comment-column)) + comment-column + 40)) + (start (region-beginning)) + (end (region-end))) + + (save-excursion + (save-restriction + (narrow-to-region start end) + (goto-char (point-min)) + (while (re-search-forward regexp nil t) + (let* ((match-start (match-beginning 0)) + (match-end (match-end 0))) + ;; 1. Find the end of the actual content before the match + ;; We move back from the match start and skip all whitespace + (save-excursion + (goto-char match-start) + (skip-chars-backward " \t") + (let ((content-end-pos (point)) + (content-end-col (current-column))) + + ;; Delete the whitespace between the content and the match + (delete-region content-end-pos match-start) + + ;; Calculate how much space we need + (if (< content-end-col target-col) + (let ((num-spaces (- target-col content-end-col))) + (insert (make-string num-spaces ?\s))) + ;; If content is already past the target, just insert one space + (insert " ")))) + + ;; Move point to the end of the match to continue searching + (goto-char match-end))))))) + +;; Optional: Bind it to a key for easier access +;; (global-set-key (kbd "C-c a l") 'my/column-align-regexp) + + +(defun diff-buffers (buffer-a buffer-b) + "Generate a diff between BUFFER-A and BUFFER-B using standard diff. +Prompts for the two buffers, defaulting to the current buffer and the +most recently active one." + (interactive + (let* ((current (current-buffer)) + (other (other-buffer current t)) + (buf-a (read-buffer "New buffer (to diff): " current t)) + (buf-b (read-buffer "Old buffer (against): " other t))) + (list (get-buffer buf-a) (get-buffer buf-b)))) + + (let ((name-a (buffer-name buffer-a)) + (name-b (buffer-name buffer-b))) + (diff buffer-b buffer-a nil t) + ;; Rename the diff output buffer to make it clear what was diffed + (when (get-buffer "*Diff*") + (with-current-buffer "*Diff*" + (rename-buffer (format "*Diff: %s vs %s*" name-b name-a) t))))) + +(defun my/list-no-prefix-bindings (keymap &optional map-name) + "Extract and display all single-key (no-prefix) bindings from KEYMAP. +Outputs a clean list into a dedicated buffer." + (interactive (list global-map "global-map")) + (let ((buf (get-buffer-create "*No-Prefix Bindings*")) + (bindings '())) + (cl-labels ((map-extract (map) + (map-keymap + (lambda (event binding) + (cond + ;; Skip nested/prefix keymaps entirely + ((keymapp binding) nil) + ;; Handle standard events (symbols or characters) + ((or (symbolp event) (integerp event)) + (let ((key-str (single-key-description event))) + ;; Filter for simple keys (no spaces, meaning no multi-key sequences) + (unless (string-match-p " " key-str) + (push (cons key-str binding) bindings)))))) + map))) + (map-extract keymap) + ;; Sort logically by key description + ;; Print the results cleanly + (with-current-buffer buf + (text-mode) + (setq buffer-read-only nil) + (erase-buffer) + (insert (format "No-Prefix Bindings for: %s\n" (or map-name "Selected Map"))) + (insert "==================================================\n\n") + (dolist (b bindings) + (insert (format "%-15s -> %s\n" (car b) (cdr b)))) + (setq buffer-read-only t)) + (pop-to-buffer buf)))) + +(provide 'utils) + +