summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmin Bandali <bandali@kelar.org>2025-02-08 10:38:38 -0500
committerAmin Bandali <bandali@kelar.org>2025-02-08 10:38:38 -0500
commit7b718d276e1d9f01287baea7187baef618e6b947 (patch)
treed641aae211c780b78b7a8d3111ed6aa70b36649e
parentf0ad1b1cbe334c004012205fdb9af81bb7c3a538 (diff)
downloadconfigs-7b718d276e1d9f01287baea7187baef618e6b947.tar.gz
configs-7b718d276e1d9f01287baea7187baef618e6b947.tar.xz
configs-7b718d276e1d9f01287baea7187baef618e6b947.zip
Add bandali-essentials and bandali-utils, use use-package
Diffstat (limited to '')
-rw-r--r--.emacs.d/init.el407
-rw-r--r--.emacs.d/lisp/bandali-essentials.el402
-rw-r--r--.emacs.d/lisp/bandali-utils.el94
3 files changed, 505 insertions, 398 deletions
diff --git a/.emacs.d/init.el b/.emacs.d/init.el
index f9518c2..7ab00be 100644
--- a/.emacs.d/init.el
+++ b/.emacs.d/init.el
@@ -113,7 +113,7 @@ plain variables. This means that `setopt' will execute any
(load custom-file 'noerror))
;; Start Emacs server
-;; (https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html)
+;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html
(run-with-idle-timer 0.5 nil #'require 'server)
(with-eval-after-load 'server
(declare-function server-edit "server")
@@ -122,361 +122,12 @@ plain variables. This means that `setopt' will execute any
(or (server-running-p) (server-mode)))
-;;; Defaults
-
-;;;; C source code
-
-(setq-default
- ;; Case-sensitive search (and `dabbrev-expand').
- ;; case-fold-search nil
- indent-tabs-mode nil ; always use space for indentation
- ;; tab-width 4
- indicate-buffer-boundaries 'left)
-
-(setq
- ;; line-spacing 3
- completion-ignore-case t
- read-buffer-completion-ignore-case t
- enable-recursive-minibuffers t
- resize-mini-windows t
- message-log-max 20000
- mode-line-compact t
- ;; mouse-autoselect-window t
- scroll-conservatively 15
- scroll-preserve-screen-position 1
- ;; I don't feel like randomly jumping out of my chair.
- ring-bell-function 'ignore)
-
-;;;; elisp source code
-
-(with-eval-after-load 'minibuffer
- (setopt read-file-name-completion-ignore-case t))
-
-(with-eval-after-load 'files
- (setopt
- make-backup-files nil
- ;; Insert newline at the end of files.
- ;; require-final-newline t
- ;; Open read-only file buffers in view-mode, to get `q' for quit.
- view-read-only t)
- (add-to-list 'auto-mode-alist '("\\README.*" . text-mode))
- (add-to-list 'auto-mode-alist '("\\.*rc$" . conf-mode))
- (add-to-list 'auto-mode-alist '("\\.bashrc$" . sh-mode))
- (add-to-list 'auto-mode-alist '("\\COMMIT_EDITMSG$" . text-mode)))
-
-(setq disabled-command-function nil)
-
-(run-with-idle-timer 0.1 nil #'require 'autorevert)
-(with-eval-after-load 'autorevert
- (setopt
- ;; auto-revert-verbose nil
- global-auto-revert-non-file-buffers nil)
- (global-auto-revert-mode 1))
-
-(run-with-idle-timer 0.1 nil #'require 'time)
-(with-eval-after-load 'time
- (setopt
- display-time-default-load-average nil
- display-time-format " %a %Y-%m-%d %-l:%M%P"
- display-time-mail-icon
- '(image :type xpm :file "gnus/gnus-pointer.xpm" :ascent center)
- display-time-use-mail-icon t
- zoneinfo-style-world-list
- `(,@zoneinfo-style-world-list
- ("Etc/UTC" "UTC")
- ("Asia/Tehran" "Tehran")
- ("Australia/Melbourne" "Melbourne")))
- (unless (display-graphic-p)
- (display-time-mode)))
-
-(defvar b/battery-format "%p%b %t")
-(run-with-idle-timer 0.1 nil #'require 'battery)
-(with-eval-after-load 'battery
- (setopt battery-mode-line-format (format " [%s]" b/battery-format))
- ;; (display-battery-mode -1)
- )
-
-(run-with-idle-timer 0.5 nil #'require 'winner)
-(with-eval-after-load 'winner
- (winner-mode 1)
- (when (featurep 'exwm)
- ;; prevent a bad interaction between EXWM and winner-mode, where
- ;; sometimes closing a window (like closing a terminal after
- ;; entering a GPG password via pinentry-gnome3's floating window)
- ;; results in a dead frame somewhere and effectively freezes EXWM.
- (advice-add
- 'winner-insert-if-new
- :around
- (lambda (orig-fun &rest args)
- ;; only add the frame if it's live
- (when (frame-live-p (car args))
- (apply orig-fun args))))))
-
-(run-with-idle-timer 0.5 nil #'require 'windmove)
-(with-eval-after-load 'windmove
- (setopt windmove-wrap-around t)
- (b/keymap-global-set "M-H" #'windmove-left)
- (b/keymap-global-set "M-L" #'windmove-right)
- (b/keymap-global-set "M-K" #'windmove-up)
- (b/keymap-global-set "M-J" #'windmove-down))
-
-(with-eval-after-load 'isearch
- (setopt
- isearch-allow-scroll t
- isearch-lazy-count t
- ;; Match non-ASCII variants during search
- search-default-mode #'char-fold-to-regexp))
-
-(b/keymap-global-set "C-x v C-=" #'vc-ediff)
-
-(with-eval-after-load 'vc-git
- (setopt
- ;; vc-git-show-stash 0
- vc-git-print-log-follow t))
-
-(with-eval-after-load 'ediff
- (setopt
- ediff-window-setup-function #'ediff-setup-windows-plain
- ediff-split-window-function #'split-window-horizontally))
-
-;; (with-eval-after-load 'face-remap
-;; (setopt
-;; ;; Gentler font resizing.
-;; text-scale-mode-step 1.05))
-
-(run-with-idle-timer 0.4 nil #'require 'mwheel)
-(with-eval-after-load 'mwheel
- (setopt
- mouse-wheel-scroll-amount '(1 ((shift) . 1)) ; one line at a time
- mouse-wheel-progressive-speed nil ; don't accelerate scrolling
- mouse-wheel-follow-mouse t)) ; scroll window under mouse
-
-(run-with-idle-timer 0.4 nil #'require 'pixel-scroll)
-(with-eval-after-load 'pixel-scroll
- (pixel-scroll-mode 1))
-
-(with-eval-after-load 'epg-config
- (setopt
- epg-gpg-program (executable-find "gpg")
- ;; Ask for GPG passphrase in minibuffer.
- ;; Will fail if gpg >= 2.1 is not available.
- epg-pinentry-mode 'loopback))
-
-;; (with-eval-after-load 'auth-source
-;; (setopt
-;; auth-sources '("~/.authinfo.gpg")
-;; authinfo-hidden
-;; (regexp-opt '("password" "client-secret" "token"))))
-
-(with-eval-after-load 'info
- (setq
- Info-directory-list
- `(,@Info-directory-list
- ,(expand-file-name
- (convert-standard-filename "info/") source-directory)
- "/usr/share/info/")))
-
-(when (display-graphic-p)
- (set-fontset-font t 'arabic "Sahel WOL")
- (let ((emoji-font "Apple Color Emoji"))
- (when (member emoji-font (font-family-list))
- (set-fontset-font
- t 'emoji `(,emoji-font . "iso10646-1") nil 'prepend)))
- (with-eval-after-load 'faces
- (let ((grey "#e7e7e7"))
- ;; (set-face-attribute 'default nil
- ;; :font "Source Code Pro"
- ;; :height 113 ; 130 ; 105
- ;; :weight 'medium)
- ;; (set-face-attribute 'fixed-pitch nil
- ;; :font "Source Code Pro"
- ;; :weight 'medium)
- ;; (set-face-attribute 'default nil
- ;; :font "Inconsolata Medium-12:hinting=true:autohint=true")
- ;; (set-face-attribute 'fixed-pitch nil
- ;; :font "Inconsolata Medium-12:hinting=true:autohint=true")
- (set-face-attribute 'default nil
- :font "Source Code Pro Medium-10.5")
- (set-face-attribute 'fixed-pitch nil
- :font "Source Code Pro Medium-10.5")
- (set-face-attribute 'mode-line nil
- :box '(:line-width 2 :style released-button)
- :background grey
- :inherit 'fixed-pitch))))
-
-(when (and (version< emacs-version "28") mode-line-compact)
- ;; Manually make some `mode-line' spaces smaller.
- ;; Emacs 28 and above do a terrific job at this out of the box
- ;; when `mode-line-compact' is set to t (see above)."
- (setq-default
- mode-line-format
- (mapcar
- (lambda (x)
- (if (and (stringp x)
- (or (string= x " ")
- (string= x " ")))
- " "
- x))
- mode-line-format)
- mode-line-buffer-identification
- (propertized-buffer-identification "%10b")))
-
-
-;;; Useful utilities
-
-(defun b/insert-asterism ()
- "Insert a centred asterism."
- (interactive)
- (let ((asterism "* * *"))
- (insert
- (concat
- "\n"
- (make-string
- (floor (/ (- fill-column (length asterism)) 2))
- ?\s)
- asterism
- "\n"))))
-
-(defun b/join-line-top ()
- "Like `join-line', but join next line to the current line."
- (interactive)
- (join-line 1))
-
-(defun b/*scratch* ()
- "Switch to `*scratch*' buffer, creating it if it does not exist."
- (interactive)
- (let ((fun (if (functionp #'get-scratch-buffer-create)
- #'get-scratch-buffer-create ; (version<= "29" emacs-version)
- #'startup--get-buffer-create-scratch))) ; (version< emacs-version "29")
- (switch-to-buffer (funcall fun))))
-
-(defun b/duplicate-line-or-region (&optional n)
- "Duplicate the current line, or region (if active).
-Make N (default: 1) copies of the current line or region."
- (interactive "*p")
- (let ((u-r-p (use-region-p)) ; if region is active
- (n1 (or n 1)))
- (save-excursion
- (let ((text
- (if u-r-p
- (buffer-substring (region-beginning) (region-end))
- (prog1 (thing-at-point 'line)
- (end-of-line)
- (if (eobp)
- (newline)
- (forward-line 1))))))
- (dotimes (_ (abs n1))
- (insert text))))))
-
-(defun b/invert-default-face (arg)
- "Invert the `default' and `mode-line' faces for the current frame.
-Swap the background and foreground for the two `default' and
-`mode-line' faces, effectively acting like a simple light/dark
-theme toggle. If prefix argument ARG is given, invert the faces
-for all frames."
- (interactive "P")
- (let ((frame (unless arg
- (selected-frame))))
- (invert-face 'default frame)
- (invert-face 'mode-line frame)
- (when (fboundp #'exwm-systemtray--refresh-background-color)
- (exwm-systemtray--refresh-background-color 'remap))))
-
-(defun b/unfill-paragraph-or-region (&optional beg end)
- "Unfill paragraph, or region (if active)."
- (interactive "r")
- (let ((fill-column most-positive-fixnum))
- (if (use-region-p)
- (fill-region beg end)
- (fill-paragraph))))
-
-
-;;; General key bindings
-
-(let ((kfs
- '(("C-c i" . ielm)
- ("C-c d" . b/duplicate-line-or-region)
- ("C-c j" . b/join-line-top)
- ("C-S-j" . b/join-line-top)
- ("C-c s c" . b/*scratch*)
- ("C-c v" . b/invert-default-face)
- ("C-c q" . b/unfill-paragraph-or-region)
- ;; evaling and macro-expanding
- ("C-c e b" . eval-buffer)
- ("C-c e e" . eval-last-sexp)
- ("C-c e m" . pp-macroexpand-last-sexp)
- ("C-c e r" . eval-region)
- ;; emacs things
- ("C-c e i" . emacs-init-time)
- ("C-c e u" . emacs-uptime)
- ("C-c e v" . emacs-version)
- ;; finding
- ("C-c f ." . find-file)
- ("C-c f l" . find-library)
- ("C-c f p" . find-file-at-point)
- ;; frames
- ("C-c F m" . make-frame-command)
- ("C-c F d" . delete-frame)
- ;; help/describe
- ("C-c h F" . describe-face))))
- (dolist (kf kfs)
- (let ((key (car kf))
- (fun (cdr kf)))
- (b/keymap-global-set key fun))))
-
-(when (display-graphic-p)
- ;; Too easy to accidentally suspend (freeze) Emacs GUI.
- (b/keymap-global-unset "C-z"))
-
-
;;; Essential packages
(add-to-list 'load-path (b/emacs.d "lisp"))
+(require 'bandali-essentials)
;; (require 'bandali-exwm)
-
-;; recently opened files
-(run-with-idle-timer 0.2 nil #'require 'recentf)
-(with-eval-after-load 'recentf
- (setopt recentf-max-saved-items 2000)
- (recentf-mode)
-
- (defun b/recentf-open ()
- "Use `completing-read' to \\[find-file] a recent file."
- (interactive)
- (find-file
- (completing-read "Find recent file: " recentf-list)))
- (b/keymap-global-set "C-c f r" #'b/recentf-open))
-
-(with-eval-after-load 'help
- (temp-buffer-resize-mode)
- (setopt help-window-select t))
-
-(with-eval-after-load 'help-mode
- (let ((m help-mode-map))
- (b/keymap-set m "p" #'backward-button)
- (b/keymap-set m "n" #'forward-button)
- (b/keymap-set m "b" #'help-go-back)
- (b/keymap-set m "f" #'help-go-forward)))
-
-(with-eval-after-load 'doc-view
- (b/keymap-set doc-view-mode-map "M-RET" #'image-previous-line))
-
-(with-eval-after-load 'shr
- (setopt shr-max-width 80))
-
-(with-eval-after-load 'mule-cmds
- (setopt default-input-method "farsi-isiri-9147"))
-
-(with-eval-after-load 'tramp
- (tramp-set-completion-function
- "ssh"
- (append (tramp-get-completion-function "ssh")
- (mapcar (lambda (file) `(tramp-parse-sconfig ,file))
- (directory-files
- "~/.ssh/config.d/"
- 'full directory-files-no-dot-files-regexp)))))
-
(require 'bandali-eshell)
(require 'bandali-ibuffer)
(require 'bandali-dired)
@@ -492,52 +143,15 @@ for all frames."
(require 'bandali-erc)
-;;; Editing
-
-;; Display Lisp objects at point in the echo area.
-(with-eval-after-load 'eldoc
- (setopt eldoc-minor-mode-string " eldoc")
- (global-eldoc-mode 1))
-
-;; highlight matching parens
-(run-with-idle-timer 0.2 nil #'require 'paren)
-(with-eval-after-load 'paren
- (show-paren-mode 1))
+;;; Programming modes
-(with-eval-after-load 'simple
- (setopt
- ;; Save what I copy into clipboard from other applications into
- ;; Emacs' kill-ring, which would allow me to still be able to
- ;; easily access it in case I kill (cut or copy) something else
- ;; inside Emacs before yanking (pasting) what I'd originally
- ;; intended to.
- save-interprogram-paste-before-kill t)
- (column-number-mode 1)
- (line-number-mode 1))
-
-(run-with-idle-timer 0.2 nil #'require 'savehist)
-(with-eval-after-load 'savehist
- ;; Save minibuffer history.
- (savehist-mode 1)
- (add-to-list 'savehist-additional-variables 'kill-ring))
-
-;; Automatically save place in files.
-(run-with-idle-timer 0.2 nil #'require 'saveplace nil 'noerror)
-(with-eval-after-load 'saveplace
- (save-place-mode 1))
-
-(with-eval-after-load 'flyspell
- (setopt flyspell-mode-line-string " fly"))
-
-(with-eval-after-load 'text-mode
- (add-hook 'text-mode-hook #'flyspell-mode)
- (b/keymap-set text-mode-map "M-RET" #'b/insert-asterism))
-
-(with-eval-after-load 'abbrev
- (add-hook 'text-mode-hook #'abbrev-mode))
+(use-package elisp-mode
+ :bind
+ ("C-c e e" . eval-last-sexp))
-
-;;; Programming modes
+(use-package pp
+ :bind
+ ("C-c e m" . pp-macroexpand-last-sexp))
(with-eval-after-load 'lisp-mode
(add-hook
@@ -594,9 +208,6 @@ for all frames."
;;; Emacs enhancements & auxiliary packages
-(with-eval-after-load 'man
- (setopt Man-width 80))
-
;; `debbugs'
(b/keymap-global-set "C-c D d" #'debbugs-gnu)
(b/keymap-global-set "C-c D b" #'debbugs-gnu-bugs)
diff --git a/.emacs.d/lisp/bandali-essentials.el b/.emacs.d/lisp/bandali-essentials.el
new file mode 100644
index 0000000..a0ea5f1
--- /dev/null
+++ b/.emacs.d/lisp/bandali-essentials.el
@@ -0,0 +1,402 @@
+;;; bandali-essentials.el --- bandali's essentials -*- lexical-binding: t; -*-
+
+;; Copyright (c) 2018-2025 Amin Bandali <bandali@gnu.org>
+
+;; Author: Amin Bandali <bandali@gnu.org>
+;; Keywords: files, internal
+
+;; 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:
+
+;; The core essentials of my GNU Emacs setup.
+
+;;; Code:
+
+(use-package emacs
+ :demand t
+ :bind
+ ("C-c e b" . eval-buffer)
+ ("C-c e r" . eval-region)
+ :config
+ (when (display-graphic-p)
+ ;; Too easy to accidentally suspend (freeze) Emacs GUI.
+ (b/keymap-global-unset "C-z"))
+
+ (setq-default
+ ;; Case-sensitive search (and `dabbrev-expand').
+ ;; case-fold-search nil
+ indent-tabs-mode nil ; always use space for indentation
+ ;; tab-width 4
+ indicate-buffer-boundaries 'left)
+
+ (setq
+ ;; line-spacing 3
+ completion-ignore-case t
+ read-buffer-completion-ignore-case t
+ enable-recursive-minibuffers t
+ resize-mini-windows t
+ message-log-max 20000
+ mode-line-compact t
+ ;; mouse-autoselect-window t
+ scroll-conservatively 15
+ scroll-preserve-screen-position 1
+ ;; I don't feel like randomly jumping out of my chair.
+ ring-bell-function 'ignore)
+
+ ;; Mode-line compacting for older Emacsen.
+ (when (and (version< emacs-version "28") mode-line-compact)
+ ;; Manually make some `mode-line' spaces smaller.
+ ;; Emacs 28 and above do a terrific job at this out of the box
+ ;; when `mode-line-compact' is set to t (see above)."
+ (setq-default
+ mode-line-format
+ (mapcar
+ (lambda (x)
+ (if (and (stringp x)
+ (or (string= x " ")
+ (string= x " ")))
+ " "
+ x))
+ mode-line-format)
+ mode-line-buffer-identification
+ (propertized-buffer-identification "%10b")))
+
+ ;; Fonts and types.
+ (when (display-graphic-p)
+ (set-fontset-font t 'arabic "Sahel WOL")
+ (let ((emoji-font "Apple Color Emoji"))
+ (when (member emoji-font (font-family-list))
+ (set-fontset-font
+ t 'emoji `(,emoji-font . "iso10646-1") nil 'prepend)))
+ (with-eval-after-load 'faces
+ (let ((grey "#e7e7e7"))
+ ;; (set-face-attribute 'default nil
+ ;; :font "Source Code Pro"
+ ;; :height 113 ; 130 ; 105
+ ;; :weight 'medium)
+ ;; (set-face-attribute 'fixed-pitch nil
+ ;; :font "Source Code Pro"
+ ;; :weight 'medium)
+ ;; (set-face-attribute 'default nil
+ ;; :font "Inconsolata Medium-12:hinting=true:autohint=true")
+ ;; (set-face-attribute 'fixed-pitch nil
+ ;; :font "Inconsolata Medium-12:hinting=true:autohint=true")
+ (set-face-attribute 'default nil
+ :font "Source Code Pro Medium-10.5")
+ (set-face-attribute 'fixed-pitch nil
+ :font "Source Code Pro Medium-10.5")
+ (set-face-attribute 'mode-line nil
+ :box '(:line-width 2 :style released-button)
+ :background grey
+ :inherit 'fixed-pitch)))))
+
+(use-package minibuffer
+ :custom
+ (read-file-name-completion-ignore-case t))
+
+(use-package files
+ :bind
+ ("C-c f ." . find-file)
+ :custom
+ (make-backup-files nil)
+ ;; Insert newline at the end of files.
+ ;; (require-final-newline t)
+ ;; Open read-only file buffers in view-mode, to get `q' for quit.
+ (view-read-only t))
+
+(use-package ffap
+ :bind
+ ("C-c f p" . find-file-at-point))
+
+(use-package find-func
+ :bind
+ ("C-c f l" . find-library))
+
+(use-package frame
+ :bind
+ ("C-c F m" . make-frame-command)
+ ("C-c F d" . delete-frame))
+
+(use-package text-mode
+ :mode "\\(README.*\\|COMMIT_EDITMSG$\\)")
+
+(use-package conf-mode
+ :mode "\\.*rc$")
+
+(use-package sh-script
+ :mode ("\\.bashrc$" . sh-mode))
+
+(use-package novice
+ :config
+ (setq disabled-command-function nil))
+
+(use-package autorevert
+ :defer 0.1
+ :custom
+ ;; (auto-revert-verbose nil)
+ (global-auto-revert-non-file-buffers nil)
+ :config
+ (global-auto-revert-mode 1))
+
+(use-package time
+ :defer 0.1
+ :bind
+ ("C-c e i" . emacs-init-time)
+ ("C-c e u" . emacs-uptime)
+ :custom
+ (display-time-default-load-average nil)
+ (display-time-format " %a %Y-%m-%d %-l:%M%P")
+ (display-time-mail-icon
+ '(image :type xpm :file "gnus/gnus-pointer.xpm" :ascent center))
+ (display-time-use-mail-icon t)
+ :config
+ (setopt
+ zoneinfo-style-world-list
+ `(,@zoneinfo-style-world-list
+ ("Etc/UTC" "UTC")
+ ("Asia/Tehran" "Tehran")
+ ("Australia/Melbourne" "Melbourne")))
+ (unless (display-graphic-p)
+ (display-time-mode 1)))
+
+(defvar b/battery-format "%p%b %t")
+(use-package battery
+ ;; :if (not (display-graphic-p))
+ :custom
+ (battery-mode-line-format (format " [%s]" b/battery-format)))
+
+(use-package winner
+ :defer 0.5
+ :config
+ (winner-mode 1)
+ (when (featurep 'exwm)
+ ;; Prevent a bad interaction between EXWM and winner-mode, where
+ ;; sometimes closing a window (like closing a terminal after
+ ;; entering a GPG password via pinentry-gnome3's floating window)
+ ;; results in a dead frame somewhere and effectively freezes EXWM.
+ (advice-add
+ 'winner-insert-if-new
+ :around
+ (lambda (orig-fun &rest args)
+ ;; only add the frame if it's live
+ (when (frame-live-p (car args))
+ (apply orig-fun args))))))
+
+(use-package windmove
+ :defer 0.5
+ :custom
+ (windmove-wrap-around t)
+ :bind
+ ("M-H" . windmove-left)
+ ("M-L" . windmove-right)
+ ("M-K" . windmove-up)
+ ("M-J" . windmove-down))
+
+(use-package isearch
+ :custom
+ (isearch-allow-scroll t)
+ (isearch-lazy-count t)
+ ;; Match non-ASCII variants during search
+ (search-default-mode #'char-fold-to-regexp))
+
+(use-package vc
+ :bind
+ ("C-x v C-=" . vc-ediff))
+
+(use-package vc-git
+ :custom
+ ;; (vc-git-show-stash 0)
+ (vc-git-print-log-follow t))
+
+(use-package ediff
+ :custom
+ (ediff-window-setup-function #'ediff-setup-windows-plain)
+ (ediff-split-window-function #'split-window-horizontally))
+
+(use-package face-remap
+ :disabled
+ :custom
+ ;; Gentler font scaling.
+ (text-scale-mode-step 1.05))
+
+(use-package mwheel
+ :defer 0.4
+ :custom
+ (mouse-wheel-scroll-amount '(1 ((shift) . 1))) ; one line at a time
+ (mouse-wheel-progressive-speed nil) ; don't accelerate scrolling
+ (mouse-wheel-follow-mouse t)) ; scroll window under mouse
+
+(use-package pixel-scroll
+ :defer 0.4
+ :config
+ (pixel-scroll-mode 1))
+
+(use-package epg-config
+ :custom
+ (epg-gpg-program (executable-find "gpg"))
+ ;; Ask for GPG passphrase in minibuffer.
+ ;; Will fail if gpg >= 2.1 is not available.
+ (epg-pinentry-mode 'loopback))
+
+(use-package auth-source
+ :disabled
+ :custom
+ (auth-sources '("~/.authinfo.gpg"))
+ (authinfo-hidden
+ (regexp-opt '("password" "client-secret" "token"))))
+
+(use-package info
+ :config
+ (setq
+ Info-directory-list
+ `(,@Info-directory-list
+ ,(expand-file-name
+ (convert-standard-filename "info/") source-directory)
+ "/usr/share/info/")))
+
+(use-package ielm
+ :bind
+ ("C-c i" . ielm))
+
+(use-package recentf
+ ;; recently opened files
+ :defer 0.2
+ :init
+ (defun b/recentf-open ()
+ "Use `completing-read' to \\[find-file] a recent file."
+ (interactive)
+ (find-file
+ (completing-read "Find recent file: " recentf-list)))
+ :bind
+ ("C-c f r" . b/recentf-open)
+ :custom
+ (recentf-max-saved-items 2000)
+ :config
+ (recentf-mode 1))
+
+(use-package help
+ :custom
+ (help-window-select t)
+ :config
+ (temp-buffer-resize-mode 1))
+
+(use-package help-mode
+ :bind
+ (:map
+ help-mode-map
+ ("p" . backward-button)
+ ("n" . forward-button)
+ ("b" . help-go-back)
+ ("f" . help-go-forward)))
+
+(use-package help-fns
+ :bind
+ ("C-c h F" . describe-face))
+
+(use-package doc-view
+ :bind
+ (:map
+ doc-view-mode-map
+ ("M-RET" . image-previous-line)))
+
+(use-package man
+ :custom
+ (Man-width 80))
+
+(use-package shr
+ :custom
+ (shr-max-width 80))
+
+(use-package mule-cmds
+ :preface (provide 'mule-cmds)
+ :custom
+ (default-input-method "farsi-isiri-9147"))
+
+(use-package tramp
+ :config
+ (tramp-set-completion-function
+ "ssh"
+ (append (tramp-get-completion-function "ssh")
+ (mapcar (lambda (file) `(tramp-parse-sconfig ,file))
+ (directory-files
+ "~/.ssh/config.d/"
+ 'full directory-files-no-dot-files-regexp)))))
+
+(use-package eldoc
+ ;; Display Lisp objects at point in the echo area.
+ :custom
+ (eldoc-minor-mode-string " eldoc")
+ :config
+ (global-eldoc-mode 1))
+
+(use-package paren
+ ;; Highlight matching parens.
+ :defer 0.2
+ :config
+ (show-paren-mode 1))
+
+(use-package simple
+ :custom
+ ;; Save what I copy into clipboard from other applications into
+ ;; Emacs' kill-ring, which would allow me to still be able to
+ ;; easily access it in case I kill (cut or copy) something else
+ ;; inside Emacs before yanking (pasting) what I'd originally
+ ;; intended to.
+ (save-interprogram-paste-before-kill t)
+ :config
+ (column-number-mode 1)
+ (line-number-mode 1))
+
+(use-package savehist
+ ;; Save minibuffer history.
+ :defer 0.2
+ :config
+ (savehist-mode 1)
+ (add-to-list 'savehist-additional-variables 'kill-ring))
+
+(use-package saveplace
+ ;; Automatically save place in files.
+ :defer 0.2
+ :config
+ (save-place-mode 1))
+
+(use-package flyspell
+ :custom
+ (flyspell-mode-line-string " fly")
+ :hook
+ (text-mode . flyspell-mode))
+
+(use-package abbrev
+ :hook
+ (text-mode . abbrev-mode))
+
+(use-package version
+ :bind
+ ("C-c e v" . emacs-version))
+
+(use-package bandali-utils
+ :bind
+ ("C-c d" . b/duplicate-line-or-region)
+ ("C-c j" . b/join-line-top)
+ ("C-S-j" . b/join-line-top)
+ ("C-c s c" . b/*scratch*)
+ ("C-c v" . b/invert-default-face)
+ ("C-c q" . b/unfill-paragraph-or-region)
+ (:map
+ text-mode-map
+ ("M-RET" . b/insert-asterism)))
+
+(provide 'bandali-essentials)
+;;; bandali-essentials.el ends here
diff --git a/.emacs.d/lisp/bandali-utils.el b/.emacs.d/lisp/bandali-utils.el
new file mode 100644
index 0000000..9edc91e
--- /dev/null
+++ b/.emacs.d/lisp/bandali-utils.el
@@ -0,0 +1,94 @@
+;;; bandali-utils.el --- useful utilities -*- lexical-binding: t; -*-
+
+;; Copyright (c) 2018-2025 Amin Bandali <bandali@gnu.org>
+
+;; Author: Amin Bandali <bandali@gnu.org>
+;; Keywords: convenience
+
+;; 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:
+
+;; Some useful utilities.
+
+;;; Code:
+
+(defun b/insert-asterism ()
+ "Insert a centred asterism."
+ (interactive)
+ (let ((asterism "* * *"))
+ (insert
+ (concat
+ "\n"
+ (make-string
+ (floor (/ (- fill-column (length asterism)) 2))
+ ?\s)
+ asterism
+ "\n"))))
+
+(defun b/join-line-top ()
+ "Like `join-line', but join next line to the current line."
+ (interactive)
+ (join-line 1))
+
+(defun b/*scratch* ()
+ "Switch to `*scratch*' buffer, creating it if it does not exist."
+ (interactive)
+ (let ((fun (if (functionp #'get-scratch-buffer-create)
+ #'get-scratch-buffer-create ; (version<= "29" emacs-version)
+ #'startup--get-buffer-create-scratch))) ; (version< emacs-version "29")
+ (switch-to-buffer (funcall fun))))
+
+(defun b/duplicate-line-or-region (&optional n)
+ "Duplicate the current line, or region (if active).
+Make N (default: 1) copies of the current line or region."
+ (interactive "*p")
+ (let ((u-r-p (use-region-p)) ; if region is active
+ (n1 (or n 1)))
+ (save-excursion
+ (let ((text
+ (if u-r-p
+ (buffer-substring (region-beginning) (region-end))
+ (prog1 (thing-at-point 'line)
+ (end-of-line)
+ (if (eobp)
+ (newline)
+ (forward-line 1))))))
+ (dotimes (_ (abs n1))
+ (insert text))))))
+
+(defun b/invert-default-face (arg)
+ "Invert the `default' and `mode-line' faces for the current frame.
+Swap the background and foreground for the two `default' and
+`mode-line' faces, effectively acting like a simple light/dark
+theme toggle. If prefix argument ARG is given, invert the faces
+for all frames."
+ (interactive "P")
+ (let ((frame (unless arg
+ (selected-frame))))
+ (invert-face 'default frame)
+ (invert-face 'mode-line frame)
+ (when (fboundp #'exwm-systemtray--refresh-background-color)
+ (exwm-systemtray--refresh-background-color 'remap))))
+
+(defun b/unfill-paragraph-or-region (&optional beg end)
+ "Unfill paragraph, or region (if active)."
+ (interactive "r")
+ (let ((fill-column most-positive-fixnum))
+ (if (use-region-p)
+ (fill-region beg end)
+ (fill-paragraph))))
+
+(provide 'bandali-utils)
+;;; bandali-utils.el ends here