diff options
author | Amin Bandali <bandali@kelar.org> | 2025-01-30 22:55:32 -0500 |
---|---|---|
committer | Amin Bandali <bandali@kelar.org> | 2025-01-30 22:55:32 -0500 |
commit | c7b47e03e0ac16d6db84d59d8cff58291b22fbaa (patch) | |
tree | 64997f5e10d25897966d496d905aa6ff835f5f94 | |
parent | 563fb78037f21c6503c6489758e8f0d86173bb7e (diff) | |
download | configs-c7b47e03e0ac16d6db84d59d8cff58291b22fbaa.tar.gz configs-c7b47e03e0ac16d6db84d59d8cff58291b22fbaa.tar.xz configs-c7b47e03e0ac16d6db84d59d8cff58291b22fbaa.zip |
Break out .emacs.d/init.el into .emacs.d/lisp/bandali-*.el again
Having used the monolithic init.el approach, I found it somewhat
unwieldy, especially as the file grows larger and larger.
-rw-r--r-- | .emacs.d/init.el | 1026 | ||||
-rw-r--r-- | .emacs.d/lisp/bandali-dired.el | 82 | ||||
-rw-r--r-- | .emacs.d/lisp/bandali-erc.el | 151 | ||||
-rw-r--r-- | .emacs.d/lisp/bandali-eshell.el | 74 | ||||
-rw-r--r-- | .emacs.d/lisp/bandali-exwm.el | 301 | ||||
-rw-r--r-- | .emacs.d/lisp/bandali-gnus.el | 392 | ||||
-rw-r--r-- | .emacs.d/lisp/bandali-ibuffer.el | 81 | ||||
-rw-r--r-- | .emacs.d/lisp/bandali-message.el | 80 | ||||
-rw-r--r-- | .emacs.d/lisp/bandali-po.el | 62 |
9 files changed, 1233 insertions, 1016 deletions
diff --git a/.emacs.d/init.el b/.emacs.d/init.el index f1b8bf9..e79be6a 100644 --- a/.emacs.d/init.el +++ b/.emacs.d/init.el @@ -22,19 +22,6 @@ ;; compatibility with a few of the recent older GNU Emacs releases ;; so I could easily reuse it on machines stuck with older Emacsen. -;; When initially putting this together, I took inspiration from -;; configurations of many great people. Some that I could remember -;; off the top of my head are: -;; -;; - https://github.com/dieggsy/dotfiles -;; - https://github.com/dakra/dmacs -;; - https://pages.sachachua.com/.emacs.d/Sacha.html -;; - https://github.com/dakrone/eos -;; - https://cce.whatthefuck.computer/ -;; - https://github.com/jwiegley/dot-emacs -;; - https://github.com/wasamasa/dotemacs -;; - https://github.com/hlissner/doom-emacs - ;;; Code: ;;; Emacs initialization @@ -459,278 +446,7 @@ for all frames." (add-to-list 'load-path (b/emacs.d "lisp")) -(when (and - (display-graphic-p) - ;; we're not running in another WM/DE - (not (or - (getenv "XDG_CURRENT_DESKTOP") - (getenv "WAYLAND_DISPLAY"))) - (member (system-name) '("adelita"))) - (add-to-list 'load-path (b/emacs.d "lisp/xelb")) - (add-to-list 'load-path (b/emacs.d "lisp/exwm")) - (require 'exwm) - - (global-set-key (kbd "C-x b") #'exwm-workspace-switch-to-buffer) - - (menu-bar-mode -1) - (tool-bar-mode -1) - - (defun b/exwm-rename-buffer () - "Make class name the buffer name, truncating beyond 60 characters." - (interactive) - (exwm-workspace-rename-buffer - (concat exwm-class-name ":" - (if (<= (length exwm-title) 60) exwm-title - (concat (substring exwm-title 0 59) "..."))))) - - (defvar b/shifted-ws-names - '(0 \) 1 \! 2 \@ 3 \# 4 \$ - 5 \% 6 \^ 7 \& 8 \* 9 \() - "Mapping of shifted numbers on my keyboard.") - - (setq - ;; Initial number of workspaces - exwm-workspace-number 4 - ;; Global keybindings - exwm-input-global-keys - `(([?\s-r] . exwm-reset) ; Reset (to line-mode) - ([?\s-/] . exwm-workspace-switch) - ([?\s-\s] . (lambda (command) - (interactive (list (read-shell-command "$ "))) - (start-process-shell-command command nil command))) - ([?\s-\\] . (lambda () - (interactive) - (start-process-shell-command - "passmenu" nil "passmenu --type"))) - ([s-return] . (lambda () - (interactive) - (start-process "" nil "xterm"))) - ([S-s-return] . (lambda () - (interactive) - (start-process "" nil "xterm" - "-name" "floating"))) - ([?\s-h] . windmove-left) - ([?\s-j] . windmove-down) - ([?\s-k] . windmove-up) - ([?\s-l] . windmove-right) - ([?\s-H] . windmove-swap-states-left) - ([?\s-J] . windmove-swap-states-down) - ([?\s-K] . windmove-swap-states-up) - ([?\s-L] . windmove-swap-states-right) - ([?\M-\s-h] . shrink-window-horizontally) - ([?\M-\s-l] . enlarge-window-horizontally) - ([?\M-\s-k] . shrink-window) - ([?\M-\s-j] . enlarge-window) - ([mode-line mouse-4] . b/exwm-ws-prev) ; up - ([mode-line mouse-5] . b/exwm-ws-next) ; down - ([mode-line mouse-6] . b/exwm-ws-prev) ; left - ([mode-line mouse-7] . b/exwm-ws-next) ; right - ([?\s-\[] . b/exwm-ws-prev) - ([?\s-\]] . b/exwm-ws-next) - ([?\s-{] . (lambda () - (interactive) - (exwm-workspace-move-window - (b/exwm-ws-prev-index)))) - ([?\s-}] . (lambda () - (interactive) - (exwm-workspace-move-window - (b/exwm-ws-next-index)))) - ,@(mapcar (lambda (i) - `(,(kbd (format "s-%d" i)) . - (lambda () - (interactive) - (exwm-workspace-switch-create ,i)))) - (number-sequence 0 (1- exwm-workspace-number))) - ,@(mapcar - (lambda (i) - `(,(kbd (format "s-%s" - (plist-get b/shifted-ws-names i))) - . - (lambda () - (interactive) - (exwm-workspace-move-window ,i)))) - (number-sequence 0 (1- exwm-workspace-number))) - ([?\s-.] . exwm-floating-toggle-floating) - ([?\s-f] . exwm-layout-toggle-fullscreen) - ([?\s-W] . (lambda () - (interactive) - (kill-buffer (current-buffer)))) - ([?\s-Q] . (lambda () - (interactive) - (exwm-manage--kill-client))) - ([?\s-\'] . (lambda () - (interactive) - (start-process-shell-command - "dmneu-light" nil "dmenu-light"))) - ([\s-XF86Back] . previous-buffer) - ([\s-XF86Forward] . next-buffer)) - ;; Line-editing shortcuts - exwm-input-simulation-keys - '(;; movement - ([?\C-b] . [left]) - ([?\M-b] . [C-left]) - ([?\C-f] . [right]) - ([?\M-f] . [C-right]) - ([?\C-p] . [up]) - ([?\C-n] . [down]) - ([?\C-a] . [home]) - ([?\C-e] . [end]) - ([?\M-v] . [prior]) - ([?\C-v] . [next]) - ([?\C-d] . [delete]) - ([?\C-k] . [S-end delete]) - ([?\M-<] . C-home) - ([?\M->] . C-end) - ;; cut/copy/paste - ([?\C-w] . [?\C-x]) - ([?\M-w] . [?\C-c]) - ([?\C-y] . [?\C-v]) - ([?\M-d] . [C-S-right ?\C-x]) - ([?\M-\d] . [C-S-left ?\C-x]) - ;; closing/quite - ([?\s-w] . [?\C-w]) - ([?\s-q] . [?\C-q]) - ;; misc - ([?\C-s] . [?\C-f]) - ([?\s-g] . [?\C-g]) - ([?\s-s] . [?\C-s]) - ([?\C-g] . [escape]) - ([?\C-/] . [?\C-z]))) - - (with-eval-after-load 'exwm-manage - (setq - exwm-manage-configurations - '(((equal exwm-instance-name "floating") - floating t - floating-mode-line nil))) - (add-hook - 'exwm-manage-finish-hook - (lambda () - (when exwm-class-name - (cond - ((member exwm-class-name '("Abrowser" "IceCat" "Firefox-esr")) - (exwm-input-set-local-simulation-keys - `(,@exwm-input-simulation-keys - ([?\C-\S-d] . [?\C-d])))) - ((member exwm-class-name '("XTerm" "Mate-terminal")) - (exwm-input-set-local-simulation-keys - '(([?\C-c ?\C-c] . [?\C-c]) - ([?\C-c ?\C-u] . [?\C-u])))) - ((string= exwm-class-name "Zathura") - (exwm-input-set-local-simulation-keys - '(([?\C-p] . [C-up]) - ([?\C-n] . [C-down]))))))))) - - ;; Enable EXWM - (exwm-enable) - (add-hook 'exwm-update-class-hook #'b/exwm-rename-buffer) - (add-hook 'exwm-update-title-hook #'b/exwm-rename-buffer) - - (defun b/fix-ido-buffer-window-other-frame () - "Fix `ido-buffer-window-other-frame'." - (defalias 'b/ido-buffer-window-other-frame-orig - (symbol-function 'ido-buffer-window-other-frame)) - (defun ido-buffer-window-other-frame (buffer) - "This is a version redefined for EXWM. - -The original one is at `b/ido-buffer-window-other-frame-orig'." - (with-current-buffer (window-buffer (selected-window)) - (if (and (derived-mode-p 'exwm-mode) - exwm--floating-frame) - ;; Switch from a floating frame. - (with-current-buffer buffer - (if (and (derived-mode-p 'exwm-mode) - exwm--floating-frame - (eq exwm--frame exwm-workspace--current)) - ;; Switch to another floating frame. - (frame-root-window exwm--floating-frame) - ;; Do not switch if the buffer is not on the current workspace. - (or (get-buffer-window buffer exwm-workspace--current) - (selected-window)))) - (with-current-buffer buffer - (when (derived-mode-p 'exwm-mode) - (if (eq exwm--frame exwm-workspace--current) - (when exwm--floating-frame - ;; Switch to a floating frame on the current workspace. - (frame-selected-window exwm--floating-frame)) - ;; Do not switch to exwm-mode buffers on other workspace (which - ;; won't work unless `exwm-layout-show-all-buffers' is set) - (unless exwm-layout-show-all-buffers - (selected-window))))))))) - (add-hook 'exwm-init-hook #'b/fix-ido-buffer-window-other-frame) - - (require 'exwm-input) - (defun b/exwm-ws-prev-index () - "Return the index for the previous EXWM workspace, wrapping -around if needed." - (if (= exwm-workspace-current-index 0) - (1- exwm-workspace-number) - (1- exwm-workspace-current-index))) - - (defun b/exwm-ws-next-index () - "Return the index for the next EXWM workspace, wrapping -around if needed." - (if (= exwm-workspace-current-index - (1- exwm-workspace-number)) - 0 - (1+ exwm-workspace-current-index))) - - (defun b/exwm-ws-prev () - "Switch to previous EXWM workspace, wrapping around if needed." - (interactive) - (exwm-workspace-switch-create - (b/exwm-ws-prev-index))) - - (defun b/exwm-ws-next () - "Switch to next EXWM workspace, wrapping around if needed." - (interactive) - (exwm-workspace-switch-create - (b/exwm-ws-next-index))) - - ;; Shorten 'C-c C-q' to 'C-q' - (define-key exwm-mode-map [?\C-q] #'exwm-input-send-next-key) - - ;; Scroll up/down/left/right on the echo area - (define-key minibuffer-inactive-mode-map [mouse-4] #'b/exwm-ws-prev) - (define-key minibuffer-inactive-mode-map [mouse-5] #'b/exwm-ws-next) - (define-key minibuffer-inactive-mode-map [mouse-6] #'b/exwm-ws-prev) - (define-key minibuffer-inactive-mode-map [mouse-7] #'b/exwm-ws-next) - - ;; (require 'exwm-randr) - ;; (setq - ;; exwm-randr-workspace-monitor-plist - ;; '(0 "eDP-1" - ;; 1 "eDP-1" 2 "eDP-1" 3 "eDP-1" - ;; 4 "eDP-1" 5 "eDP-1" 6 "eDP-1" - ;; 7 "HDMI-1" 8 "HDMI-1" 9 "HDMI-1")) - ;; ;; (add-hook - ;; ;; 'exwm-randr-screen-change-hook - ;; ;; (lambda () - ;; ;; (start-process-shell-command - ;; ;; "xrandr" nil - ;; ;; "xrandr --output HDMI-1 --mode 1280x720 --above eDP-1 --auto"))) - ;; (exwm-randr-enable) - - (require 'exwm-systemtray) - (exwm-systemtray-enable) - - ;; (add-to-list 'load-path (b/lisp "exwm-edit")) - ;; (require 'exwm-edit) - - (with-eval-after-load 'exwm-workspace - (setq exwm-workspace-show-all-buffers t) - ;; Display current EXWM workspace in mode-line - (setq-default - mode-line-format - (append - mode-line-format - '((:eval - (format - " [%s]" (number-to-string - exwm-workspace-current-index))))))) - - (with-eval-after-load 'exwm-layout - (setq exwm-layout-show-all-buffers t))) +;; (require 'bandali-exwm) ;; recently opened files (run-with-idle-timer 0.2 nil #'require 'recentf) @@ -745,159 +461,6 @@ around if needed." (completing-read "Find recent file: " recentf-list))) (b/keymap-global-set "C-c f r" #'b/recentf-open)) -(with-eval-after-load 'eshell - (setopt - eshell-hist-ignoredups t - eshell-input-filter #'eshell-input-filter-initial-space - eshell-prompt-regexp "^[^#$\n]* [#$] ; " - eshell-prompt-function - (lambda () - (let ((uid (if (functionp #'file-user-uid) - #'file-user-uid ; (version<= "30" emacs-version) - #'user-uid))) ; (version< emacs-version "30") - (concat ": " - (system-name) - ":" - (abbreviate-file-name (eshell/pwd)) - (unless (eshell-exit-success-p) - (format " [%d]" eshell-last-command-status)) - (if (= (funcall uid) 0) " # " " $ ") - "; ")))) - (eval-when-compile - (defvar eshell-prompt-regexp) - (declare-function eshell-life-is-too-much "esh-mode") - (declare-function eshell-send-input "esh-mode" - (&optional use-region queue-p no-newline))) - (defun b/eshell-quit-or-delete-char (arg) - (interactive "p") - (if (and (eolp) (looking-back eshell-prompt-regexp nil)) - (eshell-life-is-too-much) - (delete-char arg))) - (defun b/eshell-clear () - (interactive) - (let ((inhibit-read-only t)) - (erase-buffer)) - (eshell-send-input)) - (defun b/eshell-history () - (interactive) - (completing-read "Eshell history: " - (ring-elements eshell-history-ring))) - (with-eval-after-load 'esh-mode - (let ((m eshell-mode-map)) - (b/keymap-set m "C-d" #'b/eshell-quit-or-delete-char) - (b/keymap-set m "C-S-l" #'b/eshell-clear))) - (with-eval-after-load 'esh-hist - (let ((m eshell-hist-mode-map)) - (b/keymap-set m "M-r" #'b/eshell-history)))) -(b/keymap-global-set "C-c s e" #'eshell) - -(with-eval-after-load 'ibuffer - (setopt - ibuffer-saved-filter-groups - '(("default" - ("dired" (mode . dired-mode)) - ("erc" (mode . erc-mode)) - ("gnus" - (or - (mode . gnus-group-mode) - (mode . gnus-server-mode) - (mode . gnus-summary-mode) - (mode . gnus-article-mode) - (mode . message-mode))) - ("shell" - (or - (mode . eshell-mode) - (mode . shell-mode) - (mode . term-mode))) - ("tex" - (or - (mode . tex-mode) - (mode . bibtex-mode) - (mode . latex-mode))))) - ibuffer-formats - `((mark modified read-only locked - " " (name 18 18 :left :elide) - " " (size-h 9 -1 :right) - " " (mode 16 16 :left :elide) " " filename-and-process) - ,@ibuffer-formats)) - ;; Use human readable Size column instead of original one - (define-ibuffer-column size-h - (:name "Size" :inline t) - (cond - ((> (buffer-size) (* 1024 1024)) - (format "%7.1fM" (/ (buffer-size) (* 1024.0 1024.0)))) - ((> (buffer-size) (* 100 1024)) - (format "%7.0fK" (/ (buffer-size) 1024.0))) - ((> (buffer-size) 1024) - (format "%7.1fK" (/ (buffer-size) 1024.0))) - (t (format "%8d" (buffer-size))))) - - (let ((m ibuffer-mode-map)) - (b/keymap-set m "P" #'ibuffer-backward-filter-group) - (b/keymap-set m "N" #'ibuffer-forward-filter-group) - (b/keymap-set m "M-p" #'ibuffer-do-print) - (b/keymap-set m "M-n" #'ibuffer-do-shell-command-pipe-replace))) -(b/keymap-global-set "C-x C-b" #'ibuffer) -(declare-function - ibuffer-switch-to-saved-filter-groups "ibuf-ext" (name)) -(add-hook - 'ibuffer-hook - (lambda () (ibuffer-switch-to-saved-filter-groups "default"))) - -(with-eval-after-load 'dired - ;; (require 'ls-lisp) - (setopt - dired-dwim-target t - ;; dired-listing-switches "-alh --group-directories-first" - dired-listing-switches "-alh" - ;; ls-lisp-dirs-first t - ls-lisp-use-insert-directory-program nil) - - (declare-function dired-dwim-target-directory "dired-aux") - ;; easily diff 2 marked files - ;; https://oremacs.com/2017/03/18/dired-ediff/ - (defun dired-ediff-files () - (interactive) - (require 'dired-aux) - (defvar ediff-after-quit-hook-internal) - (let ((files (dired-get-marked-files)) - (wnd (current-window-configuration))) - (if (<= (length files) 2) - (let ((file1 (car files)) - (file2 (if (cdr files) - (cadr files) - (read-file-name - "file: " - (dired-dwim-target-directory))))) - (if (file-newer-than-file-p file1 file2) - (ediff-files file2 file1) - (ediff-files file1 file2)) - (add-hook 'ediff-after-quit-hook-internal - (lambda () - (setq ediff-after-quit-hook-internal nil) - (set-window-configuration wnd)))) - (error "no more than 2 files should be marked")))) - - ;; local key bindings - (let ((m dired-mode-map)) - (b/keymap-set m "b" #'dired-up-directory) - (b/keymap-set m "E" #'dired-ediff-files) - (b/keymap-set m "e" #'dired-toggle-read-only) - (b/keymap-set m "\\" #'dired-hide-details-mode)) - - (require 'dired-x) - (setopt - dired-guess-shell-alist-user - '(("\\.pdf\\'" "atril" "evince" "zathura" "okular") - ("\\.doc\\'" "libreoffice") - ("\\.docx\\'" "libreoffice") - ("\\.ppt\\'" "libreoffice") - ("\\.pptx\\'" "libreoffice") - ("\\.xls\\'" "libreoffice") - ("\\.xlsx\\'" "libreoffice") - ("\\.flac\\'" "mpv")))) -(add-hook 'dired-mode-hook #'dired-hide-details-mode) - (with-eval-after-load 'help (temp-buffer-resize-mode) (setopt help-window-select t)) @@ -927,556 +490,19 @@ around if needed." "~/.ssh/config.d/" 'full directory-files-no-dot-files-regexp))))) - -;;; Email - -(defvar b/maildir - (expand-file-name (convert-standard-filename "~/mail/"))) -(with-eval-after-load 'recentf - (add-to-list 'recentf-exclude b/maildir)) - -(setopt - mail-user-agent 'gnus-user-agent - read-mail-command #'gnus) - -(eval-when-compile - (progn - (defvar nndraft-directory) - (defvar gnus-read-newsrc-file) - (defvar gnus-save-newsrc-file) - (defvar gnus-gcc-mark-as-read) - (defvar nnmail-split-abbrev-alist))) - -(declare-function article-make-date-line "gnus-art" (date type)) - -(with-eval-after-load 'gnus - (with-eval-after-load 'nnimap - (setq nnimap-record-commands init-file-debug)) - - (setopt - gnus-select-method '(nnnil "") - gnus-secondary-select-methods - `((nnimap - "kelar" - (nnimap-stream plain) - (nnimap-address "127.0.0.1") - (nnimap-server-port 143) - (nnimap-authenticator plain) - (nnimap-user "bandali@kelar.local") - ;; (nnmail-expiry-wait immediate) - (nnmail-expiry-target nnmail-fancy-expiry-target) - (nnmail-fancy-expiry-targets - (("from" ".*" "nnimap+kelar:Archive.%Y")))) - (nnimap - "shemshak" - (nnimap-stream plain) - (nnimap-address "127.0.0.1") - (nnimap-server-port 143) - (nnimap-authenticator plain) - (nnimap-user "bandali@shemshak.local")) - (nnimap - "debian" - (nnimap-stream plain) - (nnimap-address "127.0.0.1") - (nnimap-server-port 143) - (nnimap-authenticator plain) - (nnimap-user "bandali@debian.local") - ;; (nnmail-expiry-wait immediate) - (nnmail-expiry-target nnmail-fancy-expiry-target) - (nnmail-fancy-expiry-targets - (("from" ".*" "nnimap+debian:Archive.%Y")))) - (nnimap - "gnu" - (nnimap-stream plain) - (nnimap-address "127.0.0.1") - (nnimap-server-port 143) - (nnimap-authenticator plain) - (nnimap-user "bandali@gnu.local") - (nnimap-inbox "INBOX") - (nnimap-split-methods 'nnimap-split-fancy) - (nnimap-split-fancy - (| - ;; (: gnus-registry-split-fancy-with-parent) - ;; (: gnus-group-split-fancy "INBOX" t "INBOX") - ;; spam - ("X-Spam_action" "reject" "Junk") - ;; keep debbugs emails in INBOX - (list ".*<\\(.*\\)\\.debbugs\\.gnu\\.org>.*" "INBOX") - ;; list moderation emails - (from ".+-\\(owner\\|bounces\\)@\\(non\\)?gnu\\.org" "listmod") - ;; gnu - (list ".*<\\(.*\\)\\.\\(non\\)?gnu\\.org>.*" "l.\\1") - ("Envelope-To" "emacsconf-donations@gnu.org" "l.emacsconf-donations") - ;; board-eval - (| - (list ".*<.*\\.board-eval\\.fsf\\.org>.*" "l.board-eval") - (from ".*@board-eval\\.fsf\\.org" "l.board-eval")) - ;; fsf - (list ".*<\\(.*\\)\\.fsf\\.org>.*" "l.\\1") - ;; cfarm - (from "cfarm-.*@lists\\.tetaneutral\\.net" "l.cfarm") - ;; debian - (list ".*<\\(.*\\)\\.\\(lists\\|other\\)\\.debian\\.org>.*" "l.\\1") - (list ".*<\\(.*\\)\\.alioth-lists\\.debian\\.net>.*" "l.\\1") - ;; gnus - (list ".*<\\(.*\\)\\.gnus\\.org>.*" "l.\\1") - ;; libreplanet - (list ".*<\\(.*\\)\\.libreplanet\\.org>.*" "l.\\1") - ;; iana (e.g. tz-announce) - (list ".*<\\(.*\\)\\.iana\\.org>.*" "l.\\1") - ;; mailop - (list ".*<\\(.*\\)\\.mailop\\.org>.*" "l.\\1") - ;; sdlu - (list ".*<\\(.*\\)\\.spammers\\.dontlike\\.us>.*" "l.sdlu") - ;; bitfolk - (from ".*@\\(.+\\)?bitfolk\\.com>.*" "bitfolk") - ;; haskell - (list ".*<\\(.*\\)\\.haskell\\.org>.*" "l.\\1") - ;; webmasters - (from "webmasters\\(-comment\\)?@gnu\\.org" "webmasters") - ;; other - (list ".*atreus.freelists.org" "l.atreus") - (list ".*deepspec.lists.cs.princeton.edu" "l.deepspec") - (list ".*haskell-art.we.lurk.org" "l.haskell-art") - (list ".*dev.lists.parabola.nu" "l.parabola-dev") - ;; otherwise, leave mail in INBOX - "INBOX"))) - (nnimap - "csc" - (nnimap-stream plain) - (nnimap-address "127.0.0.1") - (nnimap-server-port 143) - (nnimap-authenticator plain) - (nnimap-user "abandali@csclub.uwaterloo.local") - (nnimap-inbox "INBOX") - (nnimap-split-methods 'nnimap-split-fancy) - (nnimap-split-fancy - (| - ;; cron reports and other messages from root - (from "root@\\(.*\\.\\)?csclub\\.uwaterloo\\.ca" "INBOX") - ;; spam - ("X-Spam-Flag" "YES" "Junk") - ;; catch-all - "INBOX")))) - gnus-message-archive-group "nnimap+kelar:INBOX" - gnus-parameters - '(("l\\.fencepost-users" - (to-address . "fencepost-users@gnu.org") - (to-list . "fencepost-users@gnu.org") - (list-identifier . "\\[Fencepost-users\\]")) - ("l\\.haskell-cafe" - (to-address . "haskell-cafe@haskell.org") - (to-list . "haskell-cafe@haskell.org") - (list-identifier . "\\[Haskell-cafe\\]"))) - ;; gnus-large-newsgroup 50 - gnus-process-mark-toggle t - gnus-home-directory (b/emacs.d "gnus/") - gnus-directory - (expand-file-name - (convert-standard-filename "news/") gnus-home-directory) - gnus-interactive-exit nil - gnus-user-agent '(emacs gnus type)) - - (with-eval-after-load 'message - (setopt - message-directory - (expand-file-name - (convert-standard-filename "mail/") gnus-home-directory))) - - (with-eval-after-load 'nndraft - (setopt - nndraft-directory - (expand-file-name - (convert-standard-filename "drafts/") gnus-home-directory))) - - (when (version< emacs-version "27") - (with-eval-after-load 'nnmail - (add-to-list - 'nnmail-split-abbrev-alist - '(list . "list-id\\|list-post\\|x-mailing-list\\|x-beenthere\\|x-loop") - 'append))) - - (with-eval-after-load 'gnus-agent - (setopt gnus-agent-synchronize-flags 'ask)) - - (with-eval-after-load 'gnus-art ; article - (setopt - gnus-buttonized-mime-types - '("multipart/\\(signed\\|encrypted\\)") - gnus-sorted-header-list - '("^From:" - "^X-RT-Originator" - "^Newsgroups:" - "^Subject:" - "^Date:" - "^Envelope-To:" - "^Followup-To:" - "^Reply-To:" - "^Organization:" - "^Summary:" - "^Abstract:" - "^Keywords:" - "^To:" - "^[BGF]?Cc:" - "^Posted-To:" - "^Mail-Copies-To:" - "^Mail-Followup-To:" - "^Apparently-To:" - "^Resent-From:" - "^User-Agent:" - "^X-detected-operating-system:" - "^X-Spam_action:" - "^X-Spam_bar:" - "^Message-ID:" - ;; "^References:" - "^List-Id:" - "^Gnus-Warning:") - gnus-visible-headers - (mapconcat #'identity gnus-sorted-header-list "\\|"))) - - (with-eval-after-load 'gnus-dired - (with-eval-after-load 'dired - (add-hook 'dired-mode-hook #'gnus-dired-mode))) - - (with-eval-after-load 'gnus-group - (setopt - gnus-permanently-visible-groups "\\(:INBOX$\\|:gnu$\\)") - (add-hook 'gnus-group-mode-hook #'gnus-topic-mode) - (add-hook 'gnus-group-mode-hook #'gnus-agent-mode)) - - (with-eval-after-load 'gnus-msg - (let ((bandali "Amin Bandali%s - https://kelar.org/~bandali")) - (defvar b/csc-signature - (mapconcat - #'identity - `(,(format bandali ", MMath") - "Systems Committee <syscom@csclub.uwaterloo.ca>" - "Computer Science Club of the University of Waterloo") - "\n"))) - (setopt - gnus-gcc-mark-as-read t - gnus-message-replysign t - gnus-posting-styles - '(("nnimap\\+kelar:.*" - (address "bandali@kelar.org") - ("X-Message-SMTP-Method" "smtp mail.kelar.org 587") - (gcc "nnimap+kelar:INBOX")) - ("nnimap\\+shemshak:.*" - (address "amin@shemshak.org") - ("X-Message-SMTP-Method" "smtp mail.shemshak.org 587") - (gcc "nnimap+shemshak:Sent")) - ("nnimap\\+debian:.*" - (address "bandali@debian.org") - ("X-Message-SMTP-Method" "smtp mail-submit.debian.org 587") - (gcc "nnimap+debian:INBOX")) - ("nnimap\\+gnu:.*" - (address "bandali@gnu.org") - ("X-Message-SMTP-Method" "smtp fencepost.gnu.org 587") - (gcc "nnimap+gnu:INBOX")) - ("nnimap\\+.*:l\\.ubuntu-.*" - (address "bandali@ubuntu.com") - ("X-Message-SMTP-Method" "smtp mail.kelar.org 587")) - ((header "list-id" ".*\\.lists.ubuntu.com") - (address "bandali@ubuntu.com") - ("X-Message-SMTP-Method" "smtp mail.kelar.org 587")) - ("nnimap\\+csc:.*" - (address "bandali@csclub.uwaterloo.ca") - ("X-Message-SMTP-Method" "smtp mail.csclub.uwaterloo.ca 587") - (signature b/csc-signature) - (gcc "nnimap+csc:Sent"))))) - - ;; (require 'gnus-registry) - ;; (with-eval-after-load 'gnus-registry - ;; (setopt - ;; gnus-registry-max-entries 2500 - ;; gnus-registry-ignored-groups - ;; (append gnus-registry-ignored-groups - ;; '(("^nnimap:gnu\\.l" t) ("webmasters$" t)))) - ;; (gnus-registry-initialize)) - - (with-eval-after-load 'gnus-search - (setopt - gnus-search-use-parsed-queries t)) - - (with-eval-after-load 'gnus-start - (setopt - gnus-save-newsrc-file nil - gnus-read-newsrc-file nil) - (add-hook 'gnus-after-getting-new-news-hook #'gnus-notifications)) - - (with-eval-after-load 'gnus-sum ; summary - (setopt - gnus-thread-sort-functions - '(gnus-thread-sort-by-number - gnus-thread-sort-by-subject - gnus-thread-sort-by-date)) - (with-eval-after-load 'message - (setopt - gnus-ignored-from-addresses message-dont-reply-to-names)) - - (defun b/gnus-junk-article (&optional n) - (interactive "P" gnus-summary-mode) - (gnus-summary-move-article - n - (gnus-group-prefixed-name - "Junk" - (gnus-find-method-for-group gnus-newsgroup-name)))) - - (defvar b/gnus-summary-prefix-map) - (define-prefix-command 'b/gnus-summary-prefix-map) - (b/keymap-set - gnus-summary-mode-map "v" 'b/gnus-summary-prefix-map) - (let ((m b/gnus-summary-prefix-map)) - (b/keymap-set m "r r" #'gnus-summary-very-wide-reply) - (b/keymap-set m "r q" #'gnus-summary-very-wide-reply-with-original) - (b/keymap-set m "R r" #'gnus-summary-reply) - (b/keymap-set m "R q" #'gnus-summary-reply-with-original) - (b/keymap-set m "r a w" #'gnus-summary-show-raw-article) - (b/keymap-set m "s" #'b/gnus-junk-article))) - - (with-eval-after-load 'gnus-topic - ;; (setopt gnus-topic-line-format "%i[ %A: %(%{%n%}%) ]%v\n") - (setopt gnus-topic-line-format "%i[ %(%{%n%}%) (%A) ]%v\n") - (setq gnus-topic-topology - `(("Gnus" visible nil nil) - (("misc" visible nil nil)) - (("csc" visible nil nil)) - (("kelar" visible nil nil)) - (("shemshak" visible nil nil)) - (("debian" visible nil nil)) - (("gnu" visible nil nil)) - ;; (("old-gnu" visible nil nil)) - ))) - - (with-eval-after-load 'gnus-win - (setopt gnus-use-full-window nil)) - - (with-eval-after-load 'mm-archive - (add-to-list - 'mm-archive-decoders - '("application/gzip" nil "gunzip" "-S" ".zip" "-kd" "%f" "-r"))) - - (with-eval-after-load 'mm-decode - (setopt - ;; mm-attachment-override-types `("text/x-diff" "text/x-patch" - ;; ,@mm-attachment-override-types) - mm-discouraged-alternatives '("text/html" "text/richtext") - mm-decrypt-option 'known - mm-verify-option 'known) - (add-to-list - 'mm-inline-media-tests - `("application/gzip" mm-archive-dissect-and-inline identity)) - (add-to-list 'mm-inlined-types "application/gzip" 'append)) - - (with-eval-after-load 'mm-uu - (when (version< "27" emacs-version) - (set-face-attribute 'mm-uu-extract nil :extend t)) - (when (version< emacs-version "27") - (setopt mm-uu-diff-groups-regexp "."))) - - (with-eval-after-load 'mml - (setopt - mml-attach-file-at-the-end t - mml-content-disposition-alist - '((text - (markdown . "attachment") - (rtf . "attachment") - (t . "inline")) - (t . "attachment")))) - - (with-eval-after-load 'mml-sec - (setopt - mml-secure-openpgp-encrypt-to-self t - mml-secure-openpgp-sign-with-sender t)) - - (with-eval-after-load 'recentf - (add-to-list 'recentf-exclude gnus-home-directory))) -(b/keymap-global-set "C-c g" #'gnus-plugged) -(b/keymap-global-set "C-c G" #'gnus-unplugged) - -(with-eval-after-load 'message - ;; Redefine for a simplified In-Reply-To header - ;; (https://todo.sr.ht/~sircmpwn/lists.sr.ht/67) - (defun message-make-in-reply-to () - "Return the In-Reply-To header for this message." - (when message-reply-headers - (let ((from (mail-header-from message-reply-headers)) - (msg-id (mail-header-id message-reply-headers))) - (when from - msg-id)))) - - (setopt - message-elide-ellipsis "[...]\n" - message-citation-line-format "%N wrote:\n" - message-citation-line-function - #'message-insert-formatted-citation-line - message-confirm-send t - message-fill-column 70 - message-forward-as-mime t - ;; message-kill-buffer-on-exit t - message-send-mail-function #'smtpmail-send-it - message-subscribed-address-functions - '(gnus-find-subscribed-addresses) - message-dont-reply-to-names - (mapconcat - #'identity - '("bandali@kelar\\.org" - "amin@shemshak\\.org" - "\\(bandali\\|mab\\|aminb?\\)@gnu\\.org" - "a?bandali@\\(csclub\\.\\)?uwaterloo\\.ca" - "bandali@gnu\\.ca" - "bandali@ubuntu\\.com" - "bandali@debian\\.org") - "\\|")) - - (defun b/newlines-or-asterism (arg) - "Create newlines per my liking, or insert asterism if ARG is -non-nil." - (interactive "P") - (if arg - (b/insert-asterism) - (progn - (beginning-of-line) - (delete-region (point) (line-end-position)) - (newline) - (open-line 1)))) - (b/keymap-set message-mode-map "M-RET" #'b/newlines-or-asterism) - - (add-hook 'message-mode-hook #'flyspell-mode) - (add-hook - 'message-mode-hook (lambda () (b/keymap-local-unset "C-c C-s")))) - +(require 'bandali-eshell) +(require 'bandali-ibuffer) +(require 'bandali-dired) +;;; Email with Gnus and message +(require 'bandali-gnus) +(require 'bandali-message) ;; (with-eval-after-load 'sendmail ;; (setopt mail-header-separator "")) - ;; (with-eval-after-load 'smtpmail ;; (setopt smtpmail-queue-mail t ;; smtpmail-queue-dir (concat b/maildir "queue/"))) - - -;;; IRC -(with-eval-after-load 'erc - (setopt - erc-auto-query 'bury - erc-autojoin-domain-only nil - erc-dcc-get-default-directory (b/emacs.d "erc-dcc") - erc-email-userid "bandali" - ;; erc-join-buffer 'bury - ;; erc-lurker-hide-list '("JOIN" "PART" "QUIT") - erc-nick "bandali" - erc-prompt "erc>" - erc-prompt-for-password nil - erc-query-display 'buffer - ;; erc-server-reconnect-attempts 5 - erc-server-reconnect-timeout 3) - - (if (version< erc-version "5.6-git") - (setopt erc-format-nick-function #'erc-format-@nick) - (setopt erc-show-speaker-membership-status t)) - - (unless (version< erc-version "5.5") - (setopt erc-rename-buffers t)) - - (unless (version< erc-version "5.4") - (declare-function - erc-message "erc-backend" (message-command line &optional force)) - (declare-function erc-default-target "erc") - (declare-function erc-current-nick "erc") - (defun erc-cmd-OPME () - "Ask chanserv to op me in the current channel." - (erc-message "PRIVMSG" - (format "chanserv op %s %s" - (erc-default-target) - (erc-current-nick)) - nil)) - (declare-function erc-cmd-DEOP "erc" (&rest people)) - (defun erc-cmd-DEOPME () - "Deop myself in the current channel." - (erc-cmd-DEOP (format "%s" (erc-current-nick))))) - - (add-to-list 'erc-modules 'keep-place) - (add-to-list 'erc-modules 'log) - (when (display-graphic-p) - (add-to-list 'erc-modules 'notifications) - (add-to-list 'erc-modules 'smiley)) - (add-to-list 'erc-modules 'spelling) - - (setopt - ;; erc-enable-logging 'erc-log-all-but-server-buffers - erc-log-file-coding-system 'utf-8 - erc-log-write-after-insert t - erc-log-write-after-send t - erc-save-buffer-on-part nil - erc-save-queries-on-quit nil) - - (with-eval-after-load 'erc-match - (setopt - erc-pal-highlight-type 'nick - erc-pals - '("corwin" "^gopar" "^iank" "^rwp" "technomancy" "thomzane")) - (set-face-attribute - 'erc-pal-face nil - :foreground 'unspecified - :weight 'unspecified - :inherit 'erc-nick-default-face - :background "#ffffdf")) - - (with-eval-after-load 'erc-pcomplete - (setopt erc-pcomplete-nick-postfix ",") - ;; for matterircd nick (username) completions - ;; (advice-add - ;; #'pcomplete-erc-nicks - ;; :around - ;; (lambda (orig-fun &rest args) - ;; (let ((nicks (apply orig-fun args))) - ;; (if (string-match-p "matterircd" (symbol-name (erc-network))) - ;; (mapcar (lambda (nick) (concat "@" nick)) nicks) - ;; nicks)))) - ) - - (with-eval-after-load 'erc-stamp - (setopt - erc-timestamp-only-if-changed-flag nil - erc-timestamp-format "%T " - erc-insert-timestamp-function #'erc-insert-timestamp-left) - (set-face-attribute - 'erc-timestamp-face nil - :foreground "#aaaaaa" - :weight 'unspecified - :background 'unspecified)) - - (with-eval-after-load 'erc-track - (setopt - erc-track-enable-keybindings nil - erc-track-exclude-types - '("JOIN" "MODE" "NICK" "PART" "QUIT" - "324" "329" "332" "333" "353" "477") - erc-track-position-in-mode-line t - erc-track-priority-faces-only 'all - erc-track-shorten-function nil - erc-track-showcount t)) - - (declare-function erc-update-modules "erc") - (erc-update-modules) - - (b/keymap-global-set "C-c w e" #'erc-switch-to-buffer-other-window) - (b/keymap-set erc-mode-map "M-a" #'erc-track-switch-buffer)) -(b/keymap-global-set - "C-c e l" - (lambda () - (interactive) - (erc :id "soju-libera" - :server "localhost" - :port 6667 - :user "bandali/irc.libera.chat"))) -(b/keymap-global-set - "C-c e o" - (lambda () - (interactive) - (erc :id "soju-oftc" - :server "localhost" - :port 6667 - :user "bandali/irc.oftc.net"))) +;;; IRC with ERC +(require 'bandali-erc) ;;; Editing @@ -1636,39 +662,7 @@ non-nil." (delight 'abbrev-mode "" "abbrev") (delight 'mml-mode " mml" "mml")) -(with-eval-after-load 'po-mode - ;; Based on the `po-wrap' function from the GNUN manual: - ;; https://www.gnu.org/s/trans-coord/manual/gnun/html_node/Wrapping-Long-Lines.html - (defun b/po-wrap () - "Run the current `po-mode' buffer through `msgcat' to wrap all -lines." - (interactive) - (when (eq major-mode 'po-mode) - (let ((tmp-file (make-temp-file "po-wrap.")) - (tmp-buffer (generate-new-buffer "*temp*"))) - (unwind-protect - (progn - (write-region (point-min) (point-max) tmp-file nil 1) - (if (zerop - (call-process "msgcat" nil tmp-buffer t - (shell-quote-argument tmp-file))) - (let ((saved (point)) - (inhibit-read-only t)) - (delete-region (point-min) (point-max)) - (insert-buffer-substring tmp-buffer) - (goto-char (min saved (point-max)))) - (with-current-buffer tmp-buffer - (error (buffer-string))))) - (kill-buffer tmp-buffer) - (delete-file tmp-file))))) - - (add-hook - 'po-mode-hook (lambda () (run-with-timer 0.1 nil #'View-exit))) - (b/keymap-set po-mode-map "M-q" #'b/po-wrap)) - -(autoload #'po-mode "po-mode" - "Major mode for editing PO translation files" t) -(add-to-list 'auto-mode-alist '("\\.po\\'\\|\\.po\\." . po-mode)) +(require 'bandali-po) (add-to-list 'load-path (b/emacs.d "lisp/ffs")) (run-with-idle-timer 0.5 nil #'require 'ffs) diff --git a/.emacs.d/lisp/bandali-dired.el b/.emacs.d/lisp/bandali-dired.el new file mode 100644 index 0000000..18392c0 --- /dev/null +++ b/.emacs.d/lisp/bandali-dired.el @@ -0,0 +1,82 @@ +;;; bandali-dired.el --- bandali's dired setup -*- lexical-binding: t; -*- + +;; Copyright (c) 2018-2025 Amin Bandali <bandali@gnu.org> + +;; Author: Amin Bandali <bandali@gnu.org> +;; Keywords: files + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; My dired setup and customizations. + +;;; Code: + +(with-eval-after-load 'dired + ;; (require 'ls-lisp) + (setopt + dired-dwim-target t + ;; dired-listing-switches "-alh --group-directories-first" + dired-listing-switches "-alh" + ;; ls-lisp-dirs-first t + ls-lisp-use-insert-directory-program nil) + + (declare-function dired-dwim-target-directory "dired-aux") + ;; easily diff 2 marked files + ;; https://oremacs.com/2017/03/18/dired-ediff/ + (defun dired-ediff-files () + (interactive) + (require 'dired-aux) + (defvar ediff-after-quit-hook-internal) + (let ((files (dired-get-marked-files)) + (wnd (current-window-configuration))) + (if (<= (length files) 2) + (let ((file1 (car files)) + (file2 (if (cdr files) + (cadr files) + (read-file-name + "file: " + (dired-dwim-target-directory))))) + (if (file-newer-than-file-p file1 file2) + (ediff-files file2 file1) + (ediff-files file1 file2)) + (add-hook 'ediff-after-quit-hook-internal + (lambda () + (setq ediff-after-quit-hook-internal nil) + (set-window-configuration wnd)))) + (error "no more than 2 files should be marked")))) + + ;; local key bindings + (let ((m dired-mode-map)) + (b/keymap-set m "b" #'dired-up-directory) + (b/keymap-set m "E" #'dired-ediff-files) + (b/keymap-set m "e" #'dired-toggle-read-only) + (b/keymap-set m "\\" #'dired-hide-details-mode)) + + (require 'dired-x) + (setopt + dired-guess-shell-alist-user + '(("\\.pdf\\'" "atril" "evince" "zathura" "okular") + ("\\.doc\\'" "libreoffice") + ("\\.docx\\'" "libreoffice") + ("\\.ppt\\'" "libreoffice") + ("\\.pptx\\'" "libreoffice") + ("\\.xls\\'" "libreoffice") + ("\\.xlsx\\'" "libreoffice") + ("\\.flac\\'" "mpv")))) +(add-hook 'dired-mode-hook #'dired-hide-details-mode) + +(provide 'bandali-dired) +;;; bandali-dired.el ends here diff --git a/.emacs.d/lisp/bandali-erc.el b/.emacs.d/lisp/bandali-erc.el new file mode 100644 index 0000000..7e79329 --- /dev/null +++ b/.emacs.d/lisp/bandali-erc.el @@ -0,0 +1,151 @@ +;;; bandali-erc.el --- bandali's ERC setup -*- lexical-binding: t; -*- + +;; Copyright (c) 2018-2025 Amin Bandali <bandali@gnu.org> + +;; Author: Amin Bandali <bandali@gnu.org> +;; Keywords: IRC, chat, client, Internet + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; My ERC setup for IRC in GNU Emacs. + +;;; Code: + +(with-eval-after-load 'erc + (setopt + erc-auto-query 'bury + erc-autojoin-domain-only nil + erc-dcc-get-default-directory (b/emacs.d "erc-dcc") + erc-email-userid "bandali" + ;; erc-join-buffer 'bury + ;; erc-lurker-hide-list '("JOIN" "PART" "QUIT") + erc-nick "bandali" + erc-prompt "erc>" + erc-prompt-for-password nil + erc-query-display 'buffer + ;; erc-server-reconnect-attempts 5 + erc-server-reconnect-timeout 3) + + (if (version< erc-version "5.6-git") + (setopt erc-format-nick-function #'erc-format-@nick) + (setopt erc-show-speaker-membership-status t)) + + (unless (version< erc-version "5.5") + (setopt erc-rename-buffers t)) + + (unless (version< erc-version "5.4") + (declare-function + erc-message "erc-backend" (message-command line &optional force)) + (declare-function erc-default-target "erc") + (declare-function erc-current-nick "erc") + (defun erc-cmd-OPME () + "Ask chanserv to op me in the current channel." + (erc-message "PRIVMSG" + (format "chanserv op %s %s" + (erc-default-target) + (erc-current-nick)) + nil)) + (declare-function erc-cmd-DEOP "erc" (&rest people)) + (defun erc-cmd-DEOPME () + "Deop myself in the current channel." + (erc-cmd-DEOP (format "%s" (erc-current-nick))))) + + (add-to-list 'erc-modules 'keep-place) + (add-to-list 'erc-modules 'log) + (when (display-graphic-p) + (add-to-list 'erc-modules 'notifications) + (add-to-list 'erc-modules 'smiley)) + (add-to-list 'erc-modules 'spelling) + + (setopt + ;; erc-enable-logging 'erc-log-all-but-server-buffers + erc-log-file-coding-system 'utf-8 + erc-log-write-after-insert t + erc-log-write-after-send t + erc-save-buffer-on-part nil + erc-save-queries-on-quit nil) + + (with-eval-after-load 'erc-match + (setopt + erc-pal-highlight-type 'nick + erc-pals + '("corwin" "^gopar" "^iank" "^rwp" "technomancy" "thomzane")) + (set-face-attribute + 'erc-pal-face nil + :foreground 'unspecified + :weight 'unspecified + :inherit 'erc-nick-default-face + :background "#ffffdf")) + + (with-eval-after-load 'erc-pcomplete + (setopt erc-pcomplete-nick-postfix ",") + ;; for matterircd nick (username) completions + ;; (advice-add + ;; #'pcomplete-erc-nicks + ;; :around + ;; (lambda (orig-fun &rest args) + ;; (let ((nicks (apply orig-fun args))) + ;; (if (string-match-p "matterircd" (symbol-name (erc-network))) + ;; (mapcar (lambda (nick) (concat "@" nick)) nicks) + ;; nicks)))) + ) + + (with-eval-after-load 'erc-stamp + (setopt + erc-timestamp-only-if-changed-flag nil + erc-timestamp-format "%T " + erc-insert-timestamp-function #'erc-insert-timestamp-left) + (set-face-attribute + 'erc-timestamp-face nil + :foreground "#aaaaaa" + :weight 'unspecified + :background 'unspecified)) + + (with-eval-after-load 'erc-track + (setopt + erc-track-enable-keybindings nil + erc-track-exclude-types + '("JOIN" "MODE" "NICK" "PART" "QUIT" + "324" "329" "332" "333" "353" "477") + erc-track-position-in-mode-line t + erc-track-priority-faces-only 'all + erc-track-shorten-function nil + erc-track-showcount t)) + + (declare-function erc-update-modules "erc") + (erc-update-modules) + + (b/keymap-global-set "C-c w e" #'erc-switch-to-buffer-other-window) + (b/keymap-set erc-mode-map "M-a" #'erc-track-switch-buffer)) +(b/keymap-global-set + "C-c e l" + (lambda () + (interactive) + (erc :id "soju-libera" + :server "localhost" + :port 6667 + :user "bandali/irc.libera.chat"))) +(b/keymap-global-set + "C-c e o" + (lambda () + (interactive) + (erc :id "soju-oftc" + :server "localhost" + :port 6667 + :user "bandali/irc.oftc.net"))) + +(provide 'bandali-erc) +;;; bandali-erc.el ends here diff --git a/.emacs.d/lisp/bandali-eshell.el b/.emacs.d/lisp/bandali-eshell.el new file mode 100644 index 0000000..444c796 --- /dev/null +++ b/.emacs.d/lisp/bandali-eshell.el @@ -0,0 +1,74 @@ +;;; bandali-eshell.el --- bandali's Eshell setup -*- lexical-binding: t; -*- + +;; Copyright (C) 2018-2025 Amin Bandali <bandali@gnu.org> + +;; Author: Amin Bandali <bandali@gnu.org> +;; Keywords: processes + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; My humble Eshell setup. + +;;; Code: + +(with-eval-after-load 'eshell + (setopt + eshell-hist-ignoredups t + eshell-input-filter #'eshell-input-filter-initial-space + eshell-prompt-regexp "^[^#$\n]* [#$] ; " + eshell-prompt-function + (lambda () + (let ((uid (if (functionp #'file-user-uid) + #'file-user-uid ; (version<= "30" emacs-version) + #'user-uid))) ; (version< emacs-version "30") + (concat ": " + (system-name) + ":" + (abbreviate-file-name (eshell/pwd)) + (unless (eshell-exit-success-p) + (format " [%d]" eshell-last-command-status)) + (if (= (funcall uid) 0) " # " " $ ") + "; ")))) + (eval-when-compile + (defvar eshell-prompt-regexp) + (declare-function eshell-life-is-too-much "esh-mode") + (declare-function eshell-send-input "esh-mode" + (&optional use-region queue-p no-newline))) + (defun b/eshell-quit-or-delete-char (arg) + (interactive "p") + (if (and (eolp) (looking-back eshell-prompt-regexp nil)) + (eshell-life-is-too-much) + (delete-char arg))) + (defun b/eshell-clear () + (interactive) + (let ((inhibit-read-only t)) + (erase-buffer)) + (eshell-send-input)) + (defun b/eshell-history () + (interactive) + (completing-read "Eshell history: " + (ring-elements eshell-history-ring))) + (with-eval-after-load 'esh-mode + (let ((m eshell-mode-map)) + (b/keymap-set m "C-d" #'b/eshell-quit-or-delete-char) + (b/keymap-set m "C-S-l" #'b/eshell-clear))) + (with-eval-after-load 'esh-hist + (let ((m eshell-hist-mode-map)) + (b/keymap-set m "M-r" #'b/eshell-history)))) +(b/keymap-global-set "C-c s e" #'eshell) + +(provide 'bandali-eshell) +;;; bandali-eshell.el ends here diff --git a/.emacs.d/lisp/bandali-exwm.el b/.emacs.d/lisp/bandali-exwm.el new file mode 100644 index 0000000..d8e285a --- /dev/null +++ b/.emacs.d/lisp/bandali-exwm.el @@ -0,0 +1,301 @@ +;;; bandali-exwm.el --- bandali's EXWM configuration -*- lexical-binding: t; -*- + +;; Copyright (c) 2018-2024 Amin Bandali <bandali@gnu.org> + +;; Author: Amin Bandali <bandali@gnu.org> +;; Keywords: tools + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; My EXWM setup. Makes good use of its simulation keys. + +;;; Code: + +(when (and + (display-graphic-p) + ;; we're not running in another WM/DE + (not (or + (getenv "XDG_CURRENT_DESKTOP") + (getenv "WAYLAND_DISPLAY"))) + (member (system-name) '("adelita"))) + (add-to-list 'load-path (b/emacs.d "lisp/xelb")) + (add-to-list 'load-path (b/emacs.d "lisp/exwm")) + (require 'exwm) + + (global-set-key (kbd "C-x b") #'exwm-workspace-switch-to-buffer) + + (menu-bar-mode -1) + (tool-bar-mode -1) + + (defun b/exwm-rename-buffer () + "Make class name the buffer name, truncating beyond 60 characters." + (interactive) + (exwm-workspace-rename-buffer + (concat exwm-class-name ":" + (if (<= (length exwm-title) 60) exwm-title + (concat (substring exwm-title 0 59) "..."))))) + + (defvar b/shifted-ws-names + '(0 \) 1 \! 2 \@ 3 \# 4 \$ + 5 \% 6 \^ 7 \& 8 \* 9 \() + "Mapping of shifted numbers on my keyboard.") + + (setq + ;; Initial number of workspaces + exwm-workspace-number 4 + ;; Global keybindings + exwm-input-global-keys + `(([?\s-r] . exwm-reset) ; Reset (to line-mode) + ([?\s-/] . exwm-workspace-switch) + ([?\s-\s] . (lambda (command) + (interactive (list (read-shell-command "$ "))) + (start-process-shell-command command nil command))) + ([?\s-\\] . (lambda () + (interactive) + (start-process-shell-command + "passmenu" nil "passmenu --type"))) + ([s-return] . (lambda () + (interactive) + (start-process "" nil "xterm"))) + ([S-s-return] . (lambda () + (interactive) + (start-process "" nil "xterm" + "-name" "floating"))) + ([?\s-h] . windmove-left) + ([?\s-j] . windmove-down) + ([?\s-k] . windmove-up) + ([?\s-l] . windmove-right) + ([?\s-H] . windmove-swap-states-left) + ([?\s-J] . windmove-swap-states-down) + ([?\s-K] . windmove-swap-states-up) + ([?\s-L] . windmove-swap-states-right) + ([?\M-\s-h] . shrink-window-horizontally) + ([?\M-\s-l] . enlarge-window-horizontally) + ([?\M-\s-k] . shrink-window) + ([?\M-\s-j] . enlarge-window) + ([mode-line mouse-4] . b/exwm-ws-prev) ; up + ([mode-line mouse-5] . b/exwm-ws-next) ; down + ([mode-line mouse-6] . b/exwm-ws-prev) ; left + ([mode-line mouse-7] . b/exwm-ws-next) ; right + ([?\s-\[] . b/exwm-ws-prev) + ([?\s-\]] . b/exwm-ws-next) + ([?\s-{] . (lambda () + (interactive) + (exwm-workspace-move-window + (b/exwm-ws-prev-index)))) + ([?\s-}] . (lambda () + (interactive) + (exwm-workspace-move-window + (b/exwm-ws-next-index)))) + ,@(mapcar (lambda (i) + `(,(kbd (format "s-%d" i)) . + (lambda () + (interactive) + (exwm-workspace-switch-create ,i)))) + (number-sequence 0 (1- exwm-workspace-number))) + ,@(mapcar + (lambda (i) + `(,(kbd (format "s-%s" + (plist-get b/shifted-ws-names i))) + . + (lambda () + (interactive) + (exwm-workspace-move-window ,i)))) + (number-sequence 0 (1- exwm-workspace-number))) + ([?\s-.] . exwm-floating-toggle-floating) + ([?\s-f] . exwm-layout-toggle-fullscreen) + ([?\s-W] . (lambda () + (interactive) + (kill-buffer (current-buffer)))) + ([?\s-Q] . (lambda () + (interactive) + (exwm-manage--kill-client))) + ([?\s-\'] . (lambda () + (interactive) + (start-process-shell-command + "dmneu-light" nil "dmenu-light"))) + ([\s-XF86Back] . previous-buffer) + ([\s-XF86Forward] . next-buffer)) + ;; Line-editing shortcuts + exwm-input-simulation-keys + '(;; movement + ([?\C-b] . [left]) + ([?\M-b] . [C-left]) + ([?\C-f] . [right]) + ([?\M-f] . [C-right]) + ([?\C-p] . [up]) + ([?\C-n] . [down]) + ([?\C-a] . [home]) + ([?\C-e] . [end]) + ([?\M-v] . [prior]) + ([?\C-v] . [next]) + ([?\C-d] . [delete]) + ([?\C-k] . [S-end delete]) + ([?\M-<] . C-home) + ([?\M->] . C-end) + ;; cut/copy/paste + ([?\C-w] . [?\C-x]) + ([?\M-w] . [?\C-c]) + ([?\C-y] . [?\C-v]) + ([?\M-d] . [C-S-right ?\C-x]) + ([?\M-\d] . [C-S-left ?\C-x]) + ;; closing/quite + ([?\s-w] . [?\C-w]) + ([?\s-q] . [?\C-q]) + ;; misc + ([?\C-s] . [?\C-f]) + ([?\s-g] . [?\C-g]) + ([?\s-s] . [?\C-s]) + ([?\C-g] . [escape]) + ([?\C-/] . [?\C-z]))) + + (with-eval-after-load 'exwm-manage + (setq + exwm-manage-configurations + '(((equal exwm-instance-name "floating") + floating t + floating-mode-line nil))) + (add-hook + 'exwm-manage-finish-hook + (lambda () + (when exwm-class-name + (cond + ((member exwm-class-name '("Abrowser" "IceCat" "Firefox-esr")) + (exwm-input-set-local-simulation-keys + `(,@exwm-input-simulation-keys + ([?\C-\S-d] . [?\C-d])))) + ((member exwm-class-name '("XTerm" "Mate-terminal")) + (exwm-input-set-local-simulation-keys + '(([?\C-c ?\C-c] . [?\C-c]) + ([?\C-c ?\C-u] . [?\C-u])))) + ((string= exwm-class-name "Zathura") + (exwm-input-set-local-simulation-keys + '(([?\C-p] . [C-up]) + ([?\C-n] . [C-down]))))))))) + + ;; Enable EXWM + (exwm-enable) + (add-hook 'exwm-update-class-hook #'b/exwm-rename-buffer) + (add-hook 'exwm-update-title-hook #'b/exwm-rename-buffer) + + (defun b/fix-ido-buffer-window-other-frame () + "Fix `ido-buffer-window-other-frame'." + (defalias 'b/ido-buffer-window-other-frame-orig + (symbol-function 'ido-buffer-window-other-frame)) + (defun ido-buffer-window-other-frame (buffer) + "This is a version redefined for EXWM. + +The original one is at `b/ido-buffer-window-other-frame-orig'." + (with-current-buffer (window-buffer (selected-window)) + (if (and (derived-mode-p 'exwm-mode) + exwm--floating-frame) + ;; Switch from a floating frame. + (with-current-buffer buffer + (if (and (derived-mode-p 'exwm-mode) + exwm--floating-frame + (eq exwm--frame exwm-workspace--current)) + ;; Switch to another floating frame. + (frame-root-window exwm--floating-frame) + ;; Do not switch if the buffer is not on the current workspace. + (or (get-buffer-window buffer exwm-workspace--current) + (selected-window)))) + (with-current-buffer buffer + (when (derived-mode-p 'exwm-mode) + (if (eq exwm--frame exwm-workspace--current) + (when exwm--floating-frame + ;; Switch to a floating frame on the current workspace. + (frame-selected-window exwm--floating-frame)) + ;; Do not switch to exwm-mode buffers on other workspace (which + ;; won't work unless `exwm-layout-show-all-buffers' is set) + (unless exwm-layout-show-all-buffers + (selected-window))))))))) + (add-hook 'exwm-init-hook #'b/fix-ido-buffer-window-other-frame) + + (require 'exwm-input) + (defun b/exwm-ws-prev-index () + "Return the index for the previous EXWM workspace, wrapping +around if needed." + (if (= exwm-workspace-current-index 0) + (1- exwm-workspace-number) + (1- exwm-workspace-current-index))) + + (defun b/exwm-ws-next-index () + "Return the index for the next EXWM workspace, wrapping +around if needed." + (if (= exwm-workspace-current-index + (1- exwm-workspace-number)) + 0 + (1+ exwm-workspace-current-index))) + + (defun b/exwm-ws-prev () + "Switch to previous EXWM workspace, wrapping around if needed." + (interactive) + (exwm-workspace-switch-create + (b/exwm-ws-prev-index))) + + (defun b/exwm-ws-next () + "Switch to next EXWM workspace, wrapping around if needed." + (interactive) + (exwm-workspace-switch-create + (b/exwm-ws-next-index))) + + ;; Shorten 'C-c C-q' to 'C-q' + (define-key exwm-mode-map [?\C-q] #'exwm-input-send-next-key) + + ;; Scroll up/down/left/right on the echo area + (define-key minibuffer-inactive-mode-map [mouse-4] #'b/exwm-ws-prev) + (define-key minibuffer-inactive-mode-map [mouse-5] #'b/exwm-ws-next) + (define-key minibuffer-inactive-mode-map [mouse-6] #'b/exwm-ws-prev) + (define-key minibuffer-inactive-mode-map [mouse-7] #'b/exwm-ws-next) + + ;; (require 'exwm-randr) + ;; (setq + ;; exwm-randr-workspace-monitor-plist + ;; '(0 "eDP-1" + ;; 1 "eDP-1" 2 "eDP-1" 3 "eDP-1" + ;; 4 "eDP-1" 5 "eDP-1" 6 "eDP-1" + ;; 7 "HDMI-1" 8 "HDMI-1" 9 "HDMI-1")) + ;; ;; (add-hook + ;; ;; 'exwm-randr-screen-change-hook + ;; ;; (lambda () + ;; ;; (start-process-shell-command + ;; ;; "xrandr" nil + ;; ;; "xrandr --output HDMI-1 --mode 1280x720 --above eDP-1 --auto"))) + ;; (exwm-randr-enable) + + (require 'exwm-systemtray) + (exwm-systemtray-enable) + + ;; (add-to-list 'load-path (b/lisp "exwm-edit")) + ;; (require 'exwm-edit) + + (with-eval-after-load 'exwm-workspace + (setq exwm-workspace-show-all-buffers t) + ;; Display current EXWM workspace in mode-line + (setq-default + mode-line-format + (append + mode-line-format + '((:eval + (format + " [%s]" (number-to-string + exwm-workspace-current-index))))))) + + (with-eval-after-load 'exwm-layout + (setq exwm-layout-show-all-buffers t))) + +(provide 'bandali-exwm) +;;; bandali-exwm.el ends here diff --git a/.emacs.d/lisp/bandali-gnus.el b/.emacs.d/lisp/bandali-gnus.el new file mode 100644 index 0000000..76a689b --- /dev/null +++ b/.emacs.d/lisp/bandali-gnus.el @@ -0,0 +1,392 @@ +;;; bandali-gnus.el --- bandali's Gnus setup -*- lexical-binding: t; -*- + +;; Copyright (C) 2018-2025 Amin Bandali <bandali@gnu.org> + +;; Author: Amin Bandali <bandali@gnu.org> +;; Keywords: mail, news + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; My trusty Gnus setup. + +;;; Code: + +(defvar b/maildir + (expand-file-name (convert-standard-filename "~/mail/"))) +(with-eval-after-load 'recentf + (add-to-list 'recentf-exclude b/maildir)) + +(setopt + mail-user-agent 'gnus-user-agent + read-mail-command #'gnus) + +(eval-when-compile + (progn + (defvar nndraft-directory) + (defvar gnus-read-newsrc-file) + (defvar gnus-save-newsrc-file) + (defvar gnus-gcc-mark-as-read) + (defvar nnmail-split-abbrev-alist))) + +(declare-function article-make-date-line "gnus-art" (date type)) + +(with-eval-after-load 'gnus + (with-eval-after-load 'nnimap + (setq nnimap-record-commands init-file-debug)) + + (setopt + gnus-select-method '(nnnil "") + gnus-secondary-select-methods + `((nnimap + "kelar" + (nnimap-stream plain) + (nnimap-address "127.0.0.1") + (nnimap-server-port 143) + (nnimap-authenticator plain) + (nnimap-user "bandali@kelar.local") + ;; (nnmail-expiry-wait immediate) + (nnmail-expiry-target nnmail-fancy-expiry-target) + (nnmail-fancy-expiry-targets + (("from" ".*" "nnimap+kelar:Archive.%Y")))) + (nnimap + "shemshak" + (nnimap-stream plain) + (nnimap-address "127.0.0.1") + (nnimap-server-port 143) + (nnimap-authenticator plain) + (nnimap-user "bandali@shemshak.local")) + (nnimap + "debian" + (nnimap-stream plain) + (nnimap-address "127.0.0.1") + (nnimap-server-port 143) + (nnimap-authenticator plain) + (nnimap-user "bandali@debian.local") + ;; (nnmail-expiry-wait immediate) + (nnmail-expiry-target nnmail-fancy-expiry-target) + (nnmail-fancy-expiry-targets + (("from" ".*" "nnimap+debian:Archive.%Y")))) + (nnimap + "gnu" + (nnimap-stream plain) + (nnimap-address "127.0.0.1") + (nnimap-server-port 143) + (nnimap-authenticator plain) + (nnimap-user "bandali@gnu.local") + (nnimap-inbox "INBOX") + (nnimap-split-methods 'nnimap-split-fancy) + (nnimap-split-fancy + (| + ;; (: gnus-registry-split-fancy-with-parent) + ;; (: gnus-group-split-fancy "INBOX" t "INBOX") + ;; spam + ("X-Spam_action" "reject" "Junk") + ;; keep debbugs emails in INBOX + (list ".*<\\(.*\\)\\.debbugs\\.gnu\\.org>.*" "INBOX") + ;; list moderation emails + (from ".+-\\(owner\\|bounces\\)@\\(non\\)?gnu\\.org" "listmod") + ;; gnu + (list ".*<\\(.*\\)\\.\\(non\\)?gnu\\.org>.*" "l.\\1") + ("Envelope-To" "emacsconf-donations@gnu.org" "l.emacsconf-donations") + ;; board-eval + (| + (list ".*<.*\\.board-eval\\.fsf\\.org>.*" "l.board-eval") + (from ".*@board-eval\\.fsf\\.org" "l.board-eval")) + ;; fsf + (list ".*<\\(.*\\)\\.fsf\\.org>.*" "l.\\1") + ;; cfarm + (from "cfarm-.*@lists\\.tetaneutral\\.net" "l.cfarm") + ;; debian + (list ".*<\\(.*\\)\\.\\(lists\\|other\\)\\.debian\\.org>.*" "l.\\1") + (list ".*<\\(.*\\)\\.alioth-lists\\.debian\\.net>.*" "l.\\1") + ;; gnus + (list ".*<\\(.*\\)\\.gnus\\.org>.*" "l.\\1") + ;; libreplanet + (list ".*<\\(.*\\)\\.libreplanet\\.org>.*" "l.\\1") + ;; iana (e.g. tz-announce) + (list ".*<\\(.*\\)\\.iana\\.org>.*" "l.\\1") + ;; mailop + (list ".*<\\(.*\\)\\.mailop\\.org>.*" "l.\\1") + ;; sdlu + (list ".*<\\(.*\\)\\.spammers\\.dontlike\\.us>.*" "l.sdlu") + ;; bitfolk + (from ".*@\\(.+\\)?bitfolk\\.com>.*" "bitfolk") + ;; haskell + (list ".*<\\(.*\\)\\.haskell\\.org>.*" "l.\\1") + ;; webmasters + (from "webmasters\\(-comment\\)?@gnu\\.org" "webmasters") + ;; other + (list ".*atreus.freelists.org" "l.atreus") + (list ".*deepspec.lists.cs.princeton.edu" "l.deepspec") + (list ".*haskell-art.we.lurk.org" "l.haskell-art") + (list ".*dev.lists.parabola.nu" "l.parabola-dev") + ;; otherwise, leave mail in INBOX + "INBOX"))) + (nnimap + "csc" + (nnimap-stream plain) + (nnimap-address "127.0.0.1") + (nnimap-server-port 143) + (nnimap-authenticator plain) + (nnimap-user "abandali@csclub.uwaterloo.local") + (nnimap-inbox "INBOX") + (nnimap-split-methods 'nnimap-split-fancy) + (nnimap-split-fancy + (| + ;; cron reports and other messages from root + (from "root@\\(.*\\.\\)?csclub\\.uwaterloo\\.ca" "INBOX") + ;; spam + ("X-Spam-Flag" "YES" "Junk") + ;; catch-all + "INBOX")))) + gnus-message-archive-group "nnimap+kelar:INBOX" + gnus-parameters + '(("l\\.fencepost-users" + (to-address . "fencepost-users@gnu.org") + (to-list . "fencepost-users@gnu.org") + (list-identifier . "\\[Fencepost-users\\]")) + ("l\\.haskell-cafe" + (to-address . "haskell-cafe@haskell.org") + (to-list . "haskell-cafe@haskell.org") + (list-identifier . "\\[Haskell-cafe\\]"))) + ;; gnus-large-newsgroup 50 + gnus-process-mark-toggle t + gnus-home-directory (b/emacs.d "gnus/") + gnus-directory + (expand-file-name + (convert-standard-filename "news/") gnus-home-directory) + gnus-interactive-exit nil + gnus-user-agent '(emacs gnus type)) + + (with-eval-after-load 'message + (setopt + message-directory + (expand-file-name + (convert-standard-filename "mail/") gnus-home-directory))) + + (with-eval-after-load 'nndraft + (setopt + nndraft-directory + (expand-file-name + (convert-standard-filename "drafts/") gnus-home-directory))) + + (when (version< emacs-version "27") + (with-eval-after-load 'nnmail + (add-to-list + 'nnmail-split-abbrev-alist + '(list . "list-id\\|list-post\\|x-mailing-list\\|x-beenthere\\|x-loop") + 'append))) + + (with-eval-after-load 'gnus-agent + (setopt gnus-agent-synchronize-flags 'ask)) + + (with-eval-after-load 'gnus-art ; article + (setopt + gnus-buttonized-mime-types + '("multipart/\\(signed\\|encrypted\\)") + gnus-sorted-header-list + '("^From:" + "^X-RT-Originator" + "^Newsgroups:" + "^Subject:" + "^Date:" + "^Envelope-To:" + "^Followup-To:" + "^Reply-To:" + "^Organization:" + "^Summary:" + "^Abstract:" + "^Keywords:" + "^To:" + "^[BGF]?Cc:" + "^Posted-To:" + "^Mail-Copies-To:" + "^Mail-Followup-To:" + "^Apparently-To:" + "^Resent-From:" + "^User-Agent:" + "^X-detected-operating-system:" + "^X-Spam_action:" + "^X-Spam_bar:" + "^Message-ID:" + ;; "^References:" + "^List-Id:" + "^Gnus-Warning:") + gnus-visible-headers + (mapconcat #'identity gnus-sorted-header-list "\\|"))) + + (with-eval-after-load 'gnus-dired + (with-eval-after-load 'dired + (add-hook 'dired-mode-hook #'gnus-dired-mode))) + + (with-eval-after-load 'gnus-group + (setopt + gnus-permanently-visible-groups "\\(:INBOX$\\|:gnu$\\)") + (add-hook 'gnus-group-mode-hook #'gnus-topic-mode) + (add-hook 'gnus-group-mode-hook #'gnus-agent-mode)) + + (with-eval-after-load 'gnus-msg + (let ((bandali "Amin Bandali%s - https://kelar.org/~bandali")) + (defvar b/csc-signature + (mapconcat + #'identity + `(,(format bandali ", MMath") + "Systems Committee <syscom@csclub.uwaterloo.ca>" + "Computer Science Club of the University of Waterloo") + "\n"))) + (setopt + gnus-gcc-mark-as-read t + gnus-message-replysign t + gnus-posting-styles + '(("nnimap\\+kelar:.*" + (address "bandali@kelar.org") + ("X-Message-SMTP-Method" "smtp mail.kelar.org 587") + (gcc "nnimap+kelar:INBOX")) + ("nnimap\\+shemshak:.*" + (address "amin@shemshak.org") + ("X-Message-SMTP-Method" "smtp mail.shemshak.org 587") + (gcc "nnimap+shemshak:Sent")) + ("nnimap\\+debian:.*" + (address "bandali@debian.org") + ("X-Message-SMTP-Method" "smtp mail-submit.debian.org 587") + (gcc "nnimap+debian:INBOX")) + ("nnimap\\+gnu:.*" + (address "bandali@gnu.org") + ("X-Message-SMTP-Method" "smtp fencepost.gnu.org 587") + (gcc "nnimap+gnu:INBOX")) + ("nnimap\\+.*:l\\.ubuntu-.*" + (address "bandali@ubuntu.com") + ("X-Message-SMTP-Method" "smtp mail.kelar.org 587")) + ((header "list-id" ".*\\.lists.ubuntu.com") + (address "bandali@ubuntu.com") + ("X-Message-SMTP-Method" "smtp mail.kelar.org 587")) + ("nnimap\\+csc:.*" + (address "bandali@csclub.uwaterloo.ca") + ("X-Message-SMTP-Method" "smtp mail.csclub.uwaterloo.ca 587") + (signature b/csc-signature) + (gcc "nnimap+csc:Sent"))))) + + ;; (require 'gnus-registry) + ;; (with-eval-after-load 'gnus-registry + ;; (setopt + ;; gnus-registry-max-entries 2500 + ;; gnus-registry-ignored-groups + ;; (append gnus-registry-ignored-groups + ;; '(("^nnimap:gnu\\.l" t) ("webmasters$" t)))) + ;; (gnus-registry-initialize)) + + (with-eval-after-load 'gnus-search + (setopt + gnus-search-use-parsed-queries t)) + + (with-eval-after-load 'gnus-start + (setopt + gnus-save-newsrc-file nil + gnus-read-newsrc-file nil) + (add-hook 'gnus-after-getting-new-news-hook #'gnus-notifications)) + + (with-eval-after-load 'gnus-sum ; summary + (setopt + gnus-thread-sort-functions + '(gnus-thread-sort-by-number + gnus-thread-sort-by-subject + gnus-thread-sort-by-date)) + (with-eval-after-load 'message + (setopt + gnus-ignored-from-addresses message-dont-reply-to-names)) + + (defun b/gnus-junk-article (&optional n) + (interactive "P" gnus-summary-mode) + (gnus-summary-move-article + n + (gnus-group-prefixed-name + "Junk" + (gnus-find-method-for-group gnus-newsgroup-name)))) + + (defvar b/gnus-summary-prefix-map) + (define-prefix-command 'b/gnus-summary-prefix-map) + (b/keymap-set + gnus-summary-mode-map "v" 'b/gnus-summary-prefix-map) + (let ((m b/gnus-summary-prefix-map)) + (b/keymap-set m "r r" #'gnus-summary-very-wide-reply) + (b/keymap-set m "r q" #'gnus-summary-very-wide-reply-with-original) + (b/keymap-set m "R r" #'gnus-summary-reply) + (b/keymap-set m "R q" #'gnus-summary-reply-with-original) + (b/keymap-set m "r a w" #'gnus-summary-show-raw-article) + (b/keymap-set m "s" #'b/gnus-junk-article))) + + (with-eval-after-load 'gnus-topic + ;; (setopt gnus-topic-line-format "%i[ %A: %(%{%n%}%) ]%v\n") + (setopt gnus-topic-line-format "%i[ %(%{%n%}%) (%A) ]%v\n") + (setq gnus-topic-topology + `(("Gnus" visible nil nil) + (("misc" visible nil nil)) + (("csc" visible nil nil)) + (("kelar" visible nil nil)) + (("shemshak" visible nil nil)) + (("debian" visible nil nil)) + (("gnu" visible nil nil)) + ;; (("old-gnu" visible nil nil)) + ))) + + (with-eval-after-load 'gnus-win + (setopt gnus-use-full-window nil)) + + (with-eval-after-load 'mm-archive + (add-to-list + 'mm-archive-decoders + '("application/gzip" nil "gunzip" "-S" ".zip" "-kd" "%f" "-r"))) + + (with-eval-after-load 'mm-decode + (setopt + ;; mm-attachment-override-types `("text/x-diff" "text/x-patch" + ;; ,@mm-attachment-override-types) + mm-discouraged-alternatives '("text/html" "text/richtext") + mm-decrypt-option 'known + mm-verify-option 'known) + (add-to-list + 'mm-inline-media-tests + `("application/gzip" mm-archive-dissect-and-inline identity)) + (add-to-list 'mm-inlined-types "application/gzip" 'append)) + + (with-eval-after-load 'mm-uu + (when (version< "27" emacs-version) + (set-face-attribute 'mm-uu-extract nil :extend t)) + (when (version< emacs-version "27") + (setopt mm-uu-diff-groups-regexp "."))) + + (with-eval-after-load 'mml + (setopt + mml-attach-file-at-the-end t + mml-content-disposition-alist + '((text + (markdown . "attachment") + (rtf . "attachment") + (t . "inline")) + (t . "attachment")))) + + (with-eval-after-load 'mml-sec + (setopt + mml-secure-openpgp-encrypt-to-self t + mml-secure-openpgp-sign-with-sender t)) + + (with-eval-after-load 'recentf + (add-to-list 'recentf-exclude gnus-home-directory))) +(b/keymap-global-set "C-c g" #'gnus-plugged) +(b/keymap-global-set "C-c G" #'gnus-unplugged) + +(provide 'bandali-gnus) +;;; bandali-gnus.el ends here diff --git a/.emacs.d/lisp/bandali-ibuffer.el b/.emacs.d/lisp/bandali-ibuffer.el new file mode 100644 index 0000000..f818695 --- /dev/null +++ b/.emacs.d/lisp/bandali-ibuffer.el @@ -0,0 +1,81 @@ +;;; bandali-ibuffer.el --- bandali's Ibuffer setup -*- lexical-binding: t; -*- + +;; Copyright (C) 2018-2025 Amin Bandali <bandali@gnu.org> + +;; Author: Amin Bandali <bandali@gnu.org> +;; Keywords: tools + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; My Ibuffer setup. + +;;; Code: + +(with-eval-after-load 'ibuffer + (setopt + ibuffer-saved-filter-groups + '(("default" + ("dired" (mode . dired-mode)) + ("erc" (mode . erc-mode)) + ("gnus" + (or + (mode . gnus-group-mode) + (mode . gnus-server-mode) + (mode . gnus-summary-mode) + (mode . gnus-article-mode) + (mode . message-mode))) + ("shell" + (or + (mode . eshell-mode) + (mode . shell-mode) + (mode . term-mode))) + ("tex" + (or + (mode . tex-mode) + (mode . bibtex-mode) + (mode . latex-mode))))) + ibuffer-formats + `((mark modified read-only locked + " " (name 18 18 :left :elide) + " " (size-h 9 -1 :right) + " " (mode 16 16 :left :elide) " " filename-and-process) + ,@ibuffer-formats)) + ;; Use human readable Size column instead of original one + (define-ibuffer-column size-h + (:name "Size" :inline t) + (cond + ((> (buffer-size) (* 1024 1024)) + (format "%7.1fM" (/ (buffer-size) (* 1024.0 1024.0)))) + ((> (buffer-size) (* 100 1024)) + (format "%7.0fK" (/ (buffer-size) 1024.0))) + ((> (buffer-size) 1024) + (format "%7.1fK" (/ (buffer-size) 1024.0))) + (t (format "%8d" (buffer-size))))) + + (let ((m ibuffer-mode-map)) + (b/keymap-set m "P" #'ibuffer-backward-filter-group) + (b/keymap-set m "N" #'ibuffer-forward-filter-group) + (b/keymap-set m "M-p" #'ibuffer-do-print) + (b/keymap-set m "M-n" #'ibuffer-do-shell-command-pipe-replace))) +(b/keymap-global-set "C-x C-b" #'ibuffer) +(declare-function + ibuffer-switch-to-saved-filter-groups "ibuf-ext" (name)) +(add-hook + 'ibuffer-hook + (lambda () (ibuffer-switch-to-saved-filter-groups "default"))) + +(provide 'bandali-ibuffer) +;;; bandali-ibuffer.el ends here diff --git a/.emacs.d/lisp/bandali-message.el b/.emacs.d/lisp/bandali-message.el new file mode 100644 index 0000000..dd048bb --- /dev/null +++ b/.emacs.d/lisp/bandali-message.el @@ -0,0 +1,80 @@ +;;; bandali-message.el --- bandali's message.el setup -*- lexical-binding: t; -*- + +;; Copyright (C) 2018-2025 Amin Bandali <bandali@gnu.org> + +;; Author: Amin Bandali <bandali@gnu.org> +;; Keywords: mail, news + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; My setup for message.el. + +;;; Code: + +(with-eval-after-load 'message + ;; Redefine for a simplified In-Reply-To header + ;; (https://todo.sr.ht/~sircmpwn/lists.sr.ht/67) + (defun message-make-in-reply-to () + "Return the In-Reply-To header for this message." + (when message-reply-headers + (let ((from (mail-header-from message-reply-headers)) + (msg-id (mail-header-id message-reply-headers))) + (when from + msg-id)))) + + (setopt + message-elide-ellipsis "[...]\n" + message-citation-line-format "%N wrote:\n" + message-citation-line-function + #'message-insert-formatted-citation-line + message-confirm-send t + message-fill-column 70 + message-forward-as-mime t + ;; message-kill-buffer-on-exit t + message-send-mail-function #'smtpmail-send-it + message-subscribed-address-functions + '(gnus-find-subscribed-addresses) + message-dont-reply-to-names + (mapconcat + #'identity + '("bandali@kelar\\.org" + "amin@shemshak\\.org" + "\\(bandali\\|mab\\|aminb?\\)@gnu\\.org" + "a?bandali@\\(csclub\\.\\)?uwaterloo\\.ca" + "bandali@gnu\\.ca" + "bandali@ubuntu\\.com" + "bandali@debian\\.org") + "\\|")) + + (defun b/newlines-or-asterism (arg) + "Create newlines per my liking, or insert asterism if ARG is +non-nil." + (interactive "P") + (if arg + (b/insert-asterism) + (progn + (beginning-of-line) + (delete-region (point) (line-end-position)) + (newline) + (open-line 1)))) + (b/keymap-set message-mode-map "M-RET" #'b/newlines-or-asterism) + + (add-hook 'message-mode-hook #'flyspell-mode) + (add-hook + 'message-mode-hook (lambda () (b/keymap-local-unset "C-c C-s")))) + +(provide 'bandali-message) +;;; bandali-message.el ends here diff --git a/.emacs.d/lisp/bandali-po.el b/.emacs.d/lisp/bandali-po.el new file mode 100644 index 0000000..982225f --- /dev/null +++ b/.emacs.d/lisp/bandali-po.el @@ -0,0 +1,62 @@ +;;; bandali-po.el --- bandali's po-mode setup -*- lexical-binding: t; -*- + +;; Copyright (C) 2021 Amin Bandali <bandali@gnu.org> + +;; Author: Amin Bandali <bandali@gnu.org> +;; Keywords: i18n gettext + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; My po-mode setup for editing PO translation files in GNU Emacs. + +;;; Code: + +(with-eval-after-load 'po-mode + ;; Based on the `po-wrap' function from the GNUN manual: + ;; https://www.gnu.org/s/trans-coord/manual/gnun/html_node/Wrapping-Long-Lines.html + (defun b/po-wrap () + "Run the current `po-mode' buffer through `msgcat' to wrap all +lines." + (interactive) + (when (eq major-mode 'po-mode) + (let ((tmp-file (make-temp-file "po-wrap.")) + (tmp-buffer (generate-new-buffer "*temp*"))) + (unwind-protect + (progn + (write-region (point-min) (point-max) tmp-file nil 1) + (if (zerop + (call-process "msgcat" nil tmp-buffer t + (shell-quote-argument tmp-file))) + (let ((saved (point)) + (inhibit-read-only t)) + (delete-region (point-min) (point-max)) + (insert-buffer-substring tmp-buffer) + (goto-char (min saved (point-max)))) + (with-current-buffer tmp-buffer + (error (buffer-string))))) + (kill-buffer tmp-buffer) + (delete-file tmp-file))))) + + (add-hook + 'po-mode-hook (lambda () (run-with-timer 0.1 nil #'View-exit))) + (b/keymap-set po-mode-map "M-q" #'b/po-wrap)) + +(autoload #'po-mode "po-mode" + "Major mode for editing PO translation files" t) +(add-to-list 'auto-mode-alist '("\\.po\\'\\|\\.po\\." . po-mode)) + +(provide 'bandali-po) +;;; bandali-po.el ends here |