;; GNU Emacs configuration of Amin Bandali, computer scientist,
;; Free Software activist, and GNU maintainer & webmaster. Packages
-;; are installed through GNU Guix for a fully reproducible setup.
-;; Before switching to Guix, I used straight.el, and before that Borg.
+;; are installed through using Borg for a fully reproducible setup.
;; Over the years, I've taken inspiration from configurations of many
;; great people. Some that I can remember off the top of my head are:
\f
;;; Package management
-;; No package.el (for emacs 26 and before)
-(when (version< emacs-version "27")
- (setq package-enable-at-startup nil)
- ;; (package-initialize)
- )
-;; for emacs 27 and later, we use early-init.el. see
-;; https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=24acb31c04b4048b85311d794e600ecd7ce60d3b
-
(progn ; `borg'
(add-to-list 'load-path
(expand-file-name "lib/borg" user-emacs-directory))
;; start up emacs server. see
;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html#Emacs-Server
(use-package server
- :defer 0.4
+ :defer 0.5
:config (or (server-running-p) (server-mode)))
\f
;;; Useful utilities
-;; useful libraries
-(require 'cl-lib)
-(require 'subr-x)
-
(defmacro b/setq-every (value &rest vars)
"Set all the variables from VARS to value VALUE."
(declare (indent defun) (debug t))
(defun b/add-elisp-section ()
(interactive)
(insert "\n")
- (previous-line)
+ (forward-line -1)
(insert "\n\f\n;;; "))
;; (defvar b/fill-column 47
indent-tabs-mode nil
tab-width 4
;; cursor shape
- cursor-type 'bar)
+ cursor-type t)
;; unicode support
(comment
(display-time-mode)
:custom
(display-time-default-load-average nil)
- (display-time-format "%a %b %-e %-l:%M%P")
+ (display-time-format " %a %b %-e %-l:%M%P")
(display-time-mail-icon '(image :type xpm :file "gnus/gnus-pointer.xpm" :ascent center))
(display-time-use-mail-icon t))
:config
(display-battery-mode)
:custom
- (battery-mode-line-format " %p%% %t"))
+ (battery-mode-line-format "%p%% %t"))
(use-package fringe
:demand
:config
;; ask for GPG passphrase in minibuffer
;; this will fail if gpg>=2.1 is not available
- (if (version< "27" emacs-version)
- (setq epg-pinentry-mode 'loopback)
- (setq epa-pinentry-mode 'loopback))
+ (setq epg-pinentry-mode 'loopback)
:custom
(epg-gpg-program (executable-find "gpg")))
("C-c e u" . emacs-uptime)
("C-c e v" . emacs-version)
+ ("C-c f ." . find-file)
+ ("C-c f d" . find-name-dired)
+ ("C-c f l" . find-library)
+
("C-c F m" . make-frame-command)
("C-c F d" . delete-frame)
("C-c F D" . server-edit)
("C-S-h C" . describe-char)
("C-S-h F" . describe-face)
+ ("C-c x" . execute-extended-command)
+
("C-x k" . b/kill-current-buffer)
("C-x K" . kill-buffer)
("C-x s" . save-buffer)
\f
;;; Essential packages
+(add-to-list
+ 'load-path
+ (expand-file-name
+ (convert-standard-filename "lisp") user-emacs-directory))
+
(when b/exwm-p
(require 'bandali-exwm))
-(use-package org
- :config
- (setq org-src-tab-acts-natively t
- org-src-preserve-indentation nil
- org-edit-src-content-indentation 0
- org-link-email-description-format "Email %c: %s" ; %.30s
- org-highlight-latex-and-related '(entities)
- org-use-speed-commands t
- org-startup-folded 'content
- org-catch-invisible-edits 'show-and-error
- org-log-done 'time)
- (when (version< org-version "9.3")
- (setq org-email-link-description-format
- org-link-email-description-format))
- (add-to-list 'org-structure-template-alist '("L" . "src emacs-lisp") t)
- (add-to-list 'org-modules 'org-habit)
- :bind
- (("C-c a o a" . org-agenda)
- :map org-mode-map
- ("M-L" . org-insert-last-stored-link)
- ("M-O" . org-toggle-link-display))
- :hook ((org-mode . org-indent-mode)
- (org-mode . auto-fill-mode)
- (org-mode . flyspell-mode))
- :custom
- (org-pretty-entities t)
- (org-agenda-files '("~/usr/org/todos/personal.org"
- "~/usr/org/todos/habits.org"
- "~/src/git/masters-thesis/todo.org"))
- (org-agenda-start-on-weekday 0)
- (org-agenda-time-leading-zero t)
- (org-habit-graph-column 44)
- (org-latex-packages-alist '(("" "listings") ("" "color")))
- :custom-face
- '(org-block-begin-line ((t (:foreground "#5a5b5a" :background "#1d1f21"))))
- '(org-block ((t (:background "#1d1f21"))))
- '(org-latex-and-related ((t (:foreground "#b294bb")))))
-
-(use-package ox-latex
- :after ox
- :config
- (setq org-latex-listings 'listings
- ;; org-latex-prefer-user-labels t
- )
- (add-to-list 'org-latex-classes
- '("IEEEtran" "\\documentclass[11pt]{IEEEtran}"
- ("\\section{%s}" . "\\section*{%s}")
- ("\\subsection{%s}" . "\\subsection*{%s}")
- ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
- ("\\paragraph{%s}" . "\\paragraph*{%s}")
- ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
- t)
- (require 'ox-beamer))
-
-(use-package ox-extra
- :config
- (ox-extras-activate '(latex-header-blocks ignore-headlines)))
-
-;; asynchronous tangle, using emacs-async to asynchronously tangle an
-;; org file. closely inspired by
-;; https://github.com/dieggsy/dotfiles/tree/cc10edf7701958eff1cd94d4081da544d882a28c/emacs.d#dotfiles
-(with-eval-after-load 'org
- (defvar b/show-async-tangle-results nil
- "Keep *emacs* async buffers around for later inspection.")
-
- (defvar b/show-async-tangle-time nil
- "Show the time spent tangling the file.")
-
- (defun b/async-babel-tangle ()
- "Tangle org file asynchronously."
- (interactive)
- (let* ((file-tangle-start-time (current-time))
- (file (buffer-file-name))
- (file-nodir (file-name-nondirectory file))
- ;; (async-quiet-switch "-q")
- (file-noext (file-name-sans-extension file)))
- (async-start
- `(lambda ()
- (require 'org)
- (org-babel-tangle-file ,file))
- (unless b/show-async-tangle-results
- `(lambda (result)
- (if result
- (message "Tangled %s%s"
- ,file-nodir
- (if b/show-async-tangle-time
- (format " (%.3fs)"
- (float-time (time-subtract (current-time)
- ',file-tangle-start-time)))
- ""))
- (message "Tangling %s failed" ,file-nodir))))))))
-
-(add-to-list
- 'safe-local-variable-values
- '(eval add-hook 'after-save-hook #'b/async-babel-tangle 'append 'local))
+(require 'bandali-org)
;; *the* right way to do git
(use-package magit
- :defer 0.5
:bind (("C-x g" . magit-status)
("C-c g g" . magit-status)
("C-c g b" . magit-blame-addition)
:custom
(recentf-max-saved-items 2000))
-;; smart M-x enhancement (needed by counsel for history)
-;; (use-package smex)
-
-(bind-keys
- ("C-c f ." . find-file)
- ("C-c f d" . find-name-dired)
- ("C-c f l" . find-library)
- ;; ("C-c f r" . recentf-open-files)
- ("C-c x" . execute-extended-command))
-
-(comment
- (use-package ido
- :demand
- :bind
- (:map ido-common-completion-map
- ([escape] . minibuffer-keyboard-quit)
- ("DEL" . b/ido-backspace))
- :config
- (require 'delsel)
- (defun b/ido-backspace ()
- "Forward to `backward-delete-char'. On error (read-only), quit."
- (interactive)
- (condition-case nil
- (backward-delete-char 1)
- (error
- (minibuffer-keyboard-quit))))
- (ido-mode 1)
- (ido-everywhere 1)
- :custom
- (ido-enable-flex-matching t)
- ;; (ido-enable-regexp t)
- ;; (ido-enable-prefix t)
- (ido-max-window-height 10)
- (ido-use-virtual-buffers t))
-
- (use-package ido-vertical-mode
- :defer 0.3
- :config
- (ido-vertical-mode 1)
- :custom
- (ido-vertical-define-keys 'C-n-C-p-up-and-down)
- (ido-vertical-show-count t))
-
- (use-package ido-completing-read+
- :defer 0.3
- :after ido
- :config
- (ido-ubiquitous-mode 1))
-
- (use-package crm-custom
- :defer 0.3
- :config
- (crm-custom-mode 1))
-
- (use-package icomplete
- :defer 0.3
- :config
- (icomplete-mode 1)))
-
+;; needed for history for counsel
(use-package amx
:defer 0.3
:config
(amx-mode))
-(use-package ivy
- :defer 0.3
- :bind
- (:map ivy-minibuffer-map
- ([escape] . keyboard-escape-quit)
- ([S-up] . ivy-previous-history-element)
- ([S-down] . ivy-next-history-element)
- ("DEL" . ivy-backward-delete-char))
- :config
- (setq ivy-wrap t
- ;; ivy-height 14
- ivy-use-virtual-buffers t
- ivy-virtual-abbreviate 'abbreviate
- ivy-count-format "%d/%d ")
-
- (defvar b/ivy-ignore-buffer-modes '(magit-mode erc-mode dired-mode))
- (defun b/ivy-ignore-buffer-p (str)
- "Return non-nil if str names a buffer with a major mode
-derived from one of `b/ivy-ignore-buffer-modes'.
-
-This function is intended for use with `ivy-ignore-buffers'."
- (let* ((buf (get-buffer str))
- (mode (and buf (buffer-local-value 'major-mode buf))))
- (and mode
- (apply #'provided-mode-derived-p mode b/ivy-ignore-buffer-modes))))
- (add-to-list 'ivy-ignore-buffers 'b/ivy-ignore-buffer-p)
-
- (ivy-mode 1)
- :custom-face
- (ivy-minibuffer-match-face-1 ((t (:background "#eeeeee"))))
- (ivy-minibuffer-match-face-2 ((t (:background "#e7e7e7" :weight bold))))
- (ivy-minibuffer-match-face-3 ((t (:background "light goldenrod" :weight semi-bold))))
- (ivy-minibuffer-match-face-4 ((t (:background "misty rose" :weight semi-bold))))
- (ivy-current-match ((((class color) (background light))
- :background "#d7d7d7" :foreground "black")
- (((class color) (background dark))
- :background "#65a7e2" :foreground "black"))))
-
-(use-package swiper
- :demand
- :after ivy
- :bind (("C-S-s" . swiper-isearch)))
-
-(use-package counsel
- :demand
- :after ivy
- :bind (("C-c f r" . counsel-recentf)
- :map minibuffer-local-map
- ("C-r" . counsel-minibuffer-history))
- :config
- (counsel-mode 1)
- (defalias 'locate #'counsel-locate))
-
-(comment
-(use-package eshell
- :defer 0.5
- :commands eshell
- :bind ("C-c a s e" . eshell)
- :config
- (eval-when-compile (defvar eshell-prompt-regexp))
- (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-setup ()
- (make-local-variable 'company-idle-delay)
- (defvar company-idle-delay)
- (setq company-idle-delay nil)
- (bind-keys :map eshell-mode-map
- ("C-d" . b/eshell-quit-or-delete-char)
- ("C-S-l" . b/eshell-clear)
- ("M-r" . counsel-esh-history)
- ;; ([tab] . company-complete)
- )
- (if (version< "27" emacs-version)
- (bind-keys :map eshell-hist-mode-map
- ("M-r" . counsel-esh-history))
- (bind-keys :map eshell-mode-map
- ("M-r" . counsel-esh-history))))
+;; (require 'bandali-ido)
+(require 'bandali-ivy)
- (setq
- eshell-prompt-regexp "\\(.*\n\\)*[$#] "
- eshell-prompt-function
- (lambda ()
- (concat
- (propertize (format "%s@%s:" (user-login-name) (system-name))
- 'face 'default)
- (propertize (abbreviate-file-name default-directory)
- 'face 'font-lock-comment-face)
- (propertize "\n" 'face 'default)
- (if (= (user-uid) 0)
- (propertize "#" 'face 'red)
- (propertize "$" 'face 'default))
- (propertize " " 'face 'default))))
-
- :hook (eshell-mode . b/eshell-setup)
- :custom
- (eshell-hist-ignoredups t)
- (eshell-input-filter 'eshell-input-filter-initial-space))
+(require 'bandali-eshell)
-(use-package ibuffer
- :bind
- (("C-x C-b" . ibuffer)
- :map ibuffer-mode-map
- ("P" . ibuffer-backward-filter-group)
- ("N" . ibuffer-forward-filter-group)
- ("M-p" . ibuffer-do-print)
- ("M-n" . ibuffer-do-shell-command-pipe-replace))
- :config
- ;; Use human readable Size column instead of original one
- (define-ibuffer-column size-h
- (:name "Size" :inline t)
- (cond
- ((> (buffer-size) 1000000) (format "%7.1fM" (/ (buffer-size) 1000000.0)))
- ((> (buffer-size) 100000) (format "%7.0fk" (/ (buffer-size) 1000.0)))
- ((> (buffer-size) 1000) (format "%7.1fk" (/ (buffer-size) 1000.0)))
- (t (format "%8d" (buffer-size)))))
- :custom
- (ibuffer-saved-filter-groups
- '(("default"
- ("dired" (mode . dired-mode))
- ("org" (mode . org-mode))
- ("gnus"
- (or
- (mode . gnus-group-mode)
- (mode . gnus-summary-mode)
- (mode . gnus-article-mode)
- ;; not really, but...
- (mode . message-mode)))
- ("web"
- (or
- ;; (mode . web-mode)
- (mode . mhtml-mode)
- (mode . css-mode)
- (mode . scss-mode)
- (mode . js2-mode)))
- ("shell"
- (or
- (mode . eshell-mode)
- (mode . shell-mode)
- (mode . term-mode)))
- ("programming"
- (or
- (mode . python-mode)
- (mode . c-mode)
- (mode . c++-mode)
- (mode . java-mode)
- (mode . emacs-lisp-mode)
- (mode . scheme-mode)
- (mode . haskell-mode)
- (mode . lean-mode)
- ;; (mode . go-mode)
- (mode . alloy-mode)))
- ("tex"
- (or
- (mode . bibtex-mode)
- (mode . latex-mode)))
- ("emacs"
- (or
- (name . "^\\*scratch\\*$")
- (name . "^\\*Messages\\*$")))
- ("exwm" (mode . exwm-mode))
- ("erc" (mode . erc-mode)))))
- (ibuffer-formats
- '((mark modified read-only locked " "
- (name 72 72 :left :elide)
- " "
- (size-h 9 -1 :right)
- " "
- (mode 16 16 :left :elide)
- " " filename-and-process)
- (mark " "
- (name 16 -1)
- " " filename)))
- :hook (ibuffer . (lambda () (ibuffer-switch-to-saved-filter-groups "default"))))
+(require 'bandali-ibuffer)
(use-package outline
:disabled
("h" . outline-hide-subtree)
("s" . outline-show-subtree)))
+(comment
(use-package ls-lisp
:custom (ls-lisp-dirs-first t))
-(use-package dired
- :config
- (setq dired-dwim-target t
- dired-listing-switches "-alh"
- ls-lisp-use-insert-directory-program nil)
-
- ;; 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"))))
-
- (require 'dired-x)
- (setq dired-guess-shell-alist-user
- '(("\\.pdf\\'" "evince" "zathura" "okular")
- ("\\.doc\\'" "libreoffice")
- ("\\.docx\\'" "libreoffice")
- ("\\.ppt\\'" "libreoffice")
- ("\\.pptx\\'" "libreoffice")
- ("\\.xls\\'" "libreoffice")
- ("\\.xlsx\\'" "libreoffice")
- ("\\.flac\\'" "mpv")))
- :bind (:map dired-mode-map
- ("b" . dired-up-directory)
- ("E" . dired-ediff-files)
- ("e" . dired-toggle-read-only)
- ("\\" . dired-hide-details-mode)
- ("z" . (lambda ()
- (interactive)
- (b/dired-start-process "zathura"))))
- :hook (dired-mode . dired-hide-details-mode))
+(require 'bandali-dired)
(use-package help
:config