;;; init.el --- bandali's emacs configuration -*- lexical-binding: t -*- ;; Copyright (C) 2018-2020 Amin Bandali ;; 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 . ;;; Commentary: ;; GNU Emacs configuration of bandali, free software activist, ;; computing scientist, and GNU maintainer and volunteer. ;; 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: ;; ;; - https://github.com/dieggsy/dotfiles ;; - https://github.com/dakra/dmacs ;; - http://pages.sachachua.com/.emacs.d/Sacha.html ;; - https://github.com/dakrone/eos ;; - http://doc.rix.si/cce/cce.html ;; - https://github.com/jwiegley/dot-emacs ;; - https://github.com/wasamasa/dotemacs ;; - https://github.com/hlissner/doom-emacs ;;; Code: ;;; Emacs initialization (defvar b/before-user-init-time (current-time) "Value of `current-time' when Emacs begins loading `user-init-file'.") (defvar b/emacs-initialized nil "Whether Emacs has been initialized.") (when (not (bound-and-true-p b/emacs-initialized)) (message "Loading Emacs...done (%.3fs)" (float-time (time-subtract b/before-user-init-time before-init-time)))) ;; temporarily increase `gc-cons-threshhold' and `gc-cons-percentage' ;; during startup to reduce garbage collection frequency. clearing ;; `file-name-handler-alist' seems to help reduce startup time too. (defvar b/gc-cons-threshold gc-cons-threshold) (defvar b/gc-cons-percentage gc-cons-percentage) (defvar b/file-name-handler-alist file-name-handler-alist) (setq gc-cons-threshold (* 30 1024 1024) ; 30 MiB gc-cons-percentage 0.6 file-name-handler-alist nil ;; sidesteps a bug when profiling with esup esup-child-profile-require-level 0) ;; set them back to their defaults once we're done initializing (defun b/post-init () "My post-initialize function, run after loading `user-init-file'." (setq b/emacs-initialized t gc-cons-threshold b/gc-cons-threshold gc-cons-percentage b/gc-cons-percentage file-name-handler-alist b/file-name-handler-alist) (when (featurep 'exwm-workspace) (with-eval-after-load 'exwm-workspace (setq-default mode-line-format (append mode-line-format '((:eval (format "[%s]" (number-to-string exwm-workspace-current-index)))))))) (when (version< emacs-version "28") ;; manually make some mode-line spaces smaller ;; (version<= "28" emacs-version) can do an awesome job at this ;; out of the box if `mode-line-compact' is set to t (see below) (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")))) (add-hook 'after-init-hook #'b/post-init) ;; increase number of lines kept in *Messages* log (setq message-log-max 20000) ;;; whoami (setq user-full-name "Amin Bandali" user-mail-address "bandali@gnu.org") ;;; csetq (`custom' setq) (require 'cl-lib) (defmacro csetq (&rest args) "Set the value of user option VAR to VALUE. More generally, you can use multiple variables and values, as in (csetq VAR VALUE VAR VALUE...) This sets each user option VAR's value to the corresponding VALUE. \(fn [VAR VALUE]...)" (declare (debug setq)) `(progn ,@(cl-loop for (var value) on args by 'cddr collect `(funcall (or (get ',var 'custom-set) #'set-default) ',var ,value)))) ;;; Package management ;; variables of interest: ;; package-archive-priorities ;; package-load-list ;; package-pinned-packages ;; (let* ((b (find-file-noselect "refinery-theme.el")) ;; (d (with-current-buffer b (package-buffer-info)))) ;; (package-generate-description-file d "refinery-theme-pkg.el")) (run-with-idle-timer 0.01 nil #'require 'package) (with-eval-after-load 'package (when (= (length package-archives) 1) (csetq package-archives `(,@package-archives ;; ("bndl" . "https://p.bndl.org/elpa/") ("org" . "https://orgmode.org/elpa/")) package-load-list '(;; GNU ELPA (debbugs "0.26") (delight "1.7") (ebdb "0.6.21") (orgalist "1.13") (rt-liberation "1.31") (yasnippet "0.14.0") (expand-region "0.11.0") (emms "6.2") ;; bndl ;; (refinery-theme "0.1.1") ;; Org ELPA (org-plus-contrib "20201109")))) (package-initialize)) (csetq package-archive-upload-base "/ssh:caffeine:~/www/p/elpa") ;;; Initial setup ;; keep ~/.emacs.d clean (defvar b/etc-dir (expand-file-name (convert-standard-filename "etc/") user-emacs-directory) "The directory where packages place their configuration files.") (defvar b/var-dir (expand-file-name (convert-standard-filename "var/") user-emacs-directory) "The directory where packages place their persistent data files.") (defvar b/lisp-dir (expand-file-name (convert-standard-filename "lisp/") user-emacs-directory) "The directory where packages place their persistent data files.") (defun b/etc (file) "Expand filename FILE relative to `b/etc-dir'." (expand-file-name (convert-standard-filename file) b/etc-dir)) (defun b/var (file) "Expand filename FILE relative to `b/var-dir'." (expand-file-name (convert-standard-filename file) b/var-dir)) (defun b/lisp (file) "Expand filename FILE relative to `b/lisp-dir'." (expand-file-name (convert-standard-filename file) b/lisp-dir)) (csetq auto-save-list-file-prefix (b/var "auto-save/sessions/") nsm-settings-file (b/var "nsm-settings.el")) ;; separate custom file (don't want it mixing with init.el) (with-eval-after-load 'custom (setq custom-file (b/etc "custom.el")) (when (file-exists-p custom-file) (load custom-file)) ;; while at it, treat themes as safe ;; (setf custom-safe-themes t) ;; only one custom theme at a time ;; (defadvice load-theme (before clear-previous-themes activate) ;; "Clear existing theme settings instead of layering them" ;; (mapc #'disable-theme custom-enabled-themes)) ) ;; load the secrets file if it exists, otherwise show a warning ;; (with-demoted-errors ;; (load (b/etc "secrets"))) ;; start up emacs server. see ;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html#Emacs-Server (run-with-idle-timer 0.5 nil #'require 'server) (with-eval-after-load 'server (declare-function server-edit "server") (global-set-key (kbd "C-c F D") #'server-edit) (declare-function server-running-p "server") (or (server-running-p) (server-mode))) ;;; Defaults ;;;; C-level customizations (csetq ;; completion case sensitivity completion-ignore-case t ;; minibuffer enable-recursive-minibuffers t resize-mini-windows t ;; mode-line mode-line-compact t ;; i don't feel like jumping out of my chair every now and again; ;; so...don't *BEEP* at me, emacs =) ring-bell-function 'ignore ;; better scrolling ;; scroll-conservatively 101 scroll-conservatively 15 ;; scroll-preserve-screen-position 1 ;; focus follows mouse ;; mouse-autoselect-window t ) (setq-default ;; case-sensitive search (and `dabbrev-expand') ;; case-fold-search nil ;; always use space for indentation indent-tabs-mode nil tab-width 4) (set-fontset-font t 'arabic "Vazir") ;;;; Elisp-level customizations ;; (define-key minibuffer-local-completion-map ;; "\t" #'minibuffer-force-complete) ;; (with-eval-after-load 'icomplete ;; (setq icomplete-on-del-error-function #'abort-recursive-edit) ;; (defun b/icomplete-fido-backward-updir () ;; "Delete char before or go up directory, like `ido-mode'." ;; (interactive) ;; (if (and (eq (char-before) ?/) ;; (eq (icomplete--category) 'file)) ;; (save-excursion ;; (goto-char (1- (point))) ;; (when (search-backward "/" (point-min) t) ;; (delete-region (1+ (point)) (point-max)))) ;; (condition-case nil ;; (call-interactively #'delete-backward-char) ;; (error ;; (when icomplete-on-del-error-function ;; (funcall icomplete-on-del-error-function)))))) ;; (define-key icomplete-fido-mode-map ;; (kbd "DEL") #'b/icomplete-fido-backward-updir)) ;; (with-eval-after-load 'subr ;; (keyboard-translate ?\( ?\[) ;; (keyboard-translate ?\) ?\]) ;; (keyboard-translate ?\[ ?\() ;; (keyboard-translate ?\] ?\)) ;; ;; (keyboard-translate ?\( ?\() ;; ;; (keyboard-translate ?\) ?\)) ;; ;; (keyboard-translate ?\[ ?\[) ;; ;; (keyboard-translate ?\] ?\]) ;; ) ;; startup ;; don't need to see the startup echo area message (advice-add #'display-startup-echo-area-message :override #'ignore) (csetq ;; i want *scratch* as my startup buffer initial-buffer-choice t ;; i don't need the default hint initial-scratch-message nil ;; use customizable text-mode as major mode for *scratch* ;; (initial-major-mode 'text-mode) ;; inhibit buffer list when more than 2 files are loaded inhibit-startup-buffer-menu t ;; don't need to see the startup screen or echo area message inhibit-startup-screen t inhibit-startup-echo-area-message user-login-name) ;; files (csetq ;; backups (C-h v make-backup-files RET) backup-by-copying t backup-directory-alist (list (cons "." (b/var "backup/"))) version-control t delete-old-versions t ;; auto-save auto-save-file-name-transforms `((".*" ,(b/var "auto-save/") t)) ;; insert newline at the end of files ;; require-final-newline t ;; open read-only file buffers in view-mode ;; (enables niceties like `q' for quit) view-read-only t) ;; novice ;; disable disabled commands (csetq disabled-command-function nil) ;; lazy-person-friendly yes/no prompts (defalias 'yes-or-no-p #'y-or-n-p) ;; autorevert: enable automatic reloading of changed buffers and files (csetq auto-revert-verbose nil global-auto-revert-non-file-buffers nil) (require 'autorevert) (global-auto-revert-mode 1) ;; time and battery in mode-line (run-with-idle-timer 0.1 nil #'require 'time) (with-eval-after-load 'time (csetq display-time-default-load-average nil 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) (display-time-mode)) (run-with-idle-timer 0.1 nil #'require 'battery) (with-eval-after-load 'battery (csetq battery-mode-line-format " %p%% %t") (display-battery-mode)) ;; (with-eval-after-load 'fringe ;; ;; smaller fringe ;; (fringe-mode '(3 . 1))) ;; enable winner-mode (C-h f winner-mode RET) (require 'winner) (winner-mode 1) (with-eval-after-load 'compile ;; don't display *compilation* buffer on success. based on ;; https://stackoverflow.com/a/17788551, with changes to use `cl-letf' ;; instead of the now obsolete `flet'. (defun b/compilation-finish-function (buffer outstr) (unless (string-match "finished" outstr) (switch-to-buffer-other-window buffer)) t) (setq compilation-finish-functions #'b/compilation-finish-function) (require 'cl-macs) (defadvice compilation-start (around inhibit-display (command &optional mode name-function highlight-regexp)) (if (not (string-match "^\\(find\\|grep\\)" command)) (cl-letf (((symbol-function 'display-buffer) #'ignore)) (save-window-excursion ad-do-it)) ad-do-it)) (ad-activate 'compilation-start)) ;; isearch (csetq ;; allow scrolling in Isearch isearch-allow-scroll t isearch-lazy-count t ;; search for non-ASCII characters: i’d like non-ASCII characters such ;; as ‘’“”«»‹›áⓐ𝒶 to be selected when i search for their ASCII ;; counterpart. shoutout to ;; http://endlessparentheses.com/new-in-emacs-25-1-easily-search-non-ascii-characters.html search-default-mode #'char-fold-to-regexp) ;; replace ;; uncomment to extend the above behaviour to query-replace ;; (csetq replace-char-fold t) ;; vc (global-set-key (kbd "C-x v C-=") #'vc-ediff) (with-eval-after-load 'vc-git (csetq vc-git-print-log-follow t vc-git-show-stash 0)) (csetq ediff-window-setup-function 'ediff-setup-windows-plain ediff-split-window-function 'split-window-horizontally) (with-eval-after-load 'ediff (add-hook 'ediff-after-quit-hook-internal #'winner-undo)) ;; face-remap (csetq ;; gentler font resizing text-scale-mode-step 1.05) (run-with-idle-timer 0.4 nil #'require 'mwheel) (csetq 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)) ;; epg-config (csetq epg-gpg-program (executable-find "gpg") ;; ask for GPG passphrase in minibuffer ;; this will fail if gpg>=2.1 is not available epg-pinentry-mode 'loopback) ;; (require 'pinentry) ;; workaround for systemd-based distros: ;; (setq pinentry--socket-dir server-socket-dir) ;; (pinentry-start) ;; auth-source (csetq auth-sources '("~/.authinfo.gpg") authinfo-hidden (regexp-opt '("password" "client-secret" "token"))) ;; info (with-eval-after-load 'info (add-to-list 'Info-directory-list (expand-file-name (convert-standard-filename "info/") source-directory))) ;; faces (when (display-graphic-p) (with-eval-after-load 'faces (let* ((grey "#e7e7e7") (darker-grey "#d9d9d9") (box ;; `(:line-width -1 :style released-button) 'unspecified)) (set-face-attribute 'mode-line nil :background grey :box box) (set-face-attribute 'mode-line-inactive nil :background darker-grey :box box)))) ;;; Useful utilities (defun b/add-elisp-section () (interactive) (insert "\n") (forward-line -1) (insert "\n \n;;; ")) (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/start-process (program &rest args) "Same as `start-process', but doesn't bother about name and buffer." (let ((process-name (concat program "_process")) (buffer-name (generate-new-buffer-name (concat program "_output")))) (apply #'start-process process-name buffer-name program args))) (defun b/no-mouse-autoselect-window () "Conveniently disable `focus-follows-mouse'. For disabling the behaviour for certain buffers and/or modes." (make-local-variable 'mouse-autoselect-window) (setq mouse-autoselect-window nil)) (defun b/kill-current-buffer () "Kill the current buffer." ;; also see https://redd.it/64xb3q (interactive) (kill-buffer (current-buffer))) (defun b/move-indentation-or-beginning-of-line (arg) "Move to the indentation or to the beginning of line." (interactive "^p") ;; (if (bolp) ;; (back-to-indentation) ;; (move-beginning-of-line arg)) (if (= (point) (progn (back-to-indentation) (point))) (move-beginning-of-line arg))) (defun b/join-line-top () "Like `join-line', but join next line to the current line." (interactive) (join-line 1)) (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)))))) ;;; General key bindings (global-set-key (kbd "C-a") #'b/move-indentation-or-beginning-of-line) (global-set-key (kbd "C-c a i") #'ielm) (global-set-key (kbd "C-c d") #'b/duplicate-line-or-region) (global-set-key (kbd "C-c j") #'b/join-line-top) (global-set-key (kbd "C-S-j") #'b/join-line-top) (global-set-key (kbd "C-c x") #'execute-extended-command) ;; evaling and macro-expanding (global-set-key (kbd "C-c e b") #'eval-buffer) (global-set-key (kbd "C-c e e") #'eval-last-sexp) (global-set-key (kbd "C-c e m") #'pp-macroexpand-last-sexp) (global-set-key (kbd "C-c e r") #'eval-region) ;; emacs things (global-set-key (kbd "C-c e i") #'emacs-init-time) (global-set-key (kbd "C-c e u") #'emacs-uptime) (global-set-key (kbd "C-c e v") #'emacs-version) ;; finding (global-set-key (kbd "C-c f .") #'find-file) (global-set-key (kbd "C-c f d") #'find-name-dired) (global-set-key (kbd "C-c f l") #'find-library) ;; frames (global-set-key (kbd "C-c F m") #'make-frame-command) (global-set-key (kbd "C-c F d") #'delete-frame) ;; help/describe (global-set-key (kbd "C-S-h F") #'describe-face) ;; (global-set-key (kbd "C-x k") #'b/kill-current-buffer) ;; (global-set-key (kbd "C-x K") #'kill-buffer) (define-key emacs-lisp-mode-map (kbd "C-") #'b/add-elisp-section) (when (display-graphic-p) (global-unset-key (kbd "C-z"))) ;;; Essential packages (add-to-list 'load-path (expand-file-name (convert-standard-filename "lisp") user-emacs-directory)) ;; (require 'bandali-exwm) (require 'bandali-org) ;; (require 'bandali-theme) ;; recently opened files (csetq recentf-max-saved-items 2000 recentf-save-file (b/var "recentf-save.el")) (run-with-idle-timer 0.2 nil #'require 'recentf) (with-eval-after-load 'recentf ;; (add-to-list 'recentf-keep #'file-remote-p) (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))) (global-set-key (kbd "C-c f r") #'b/recentf-open)) (fido-mode 1) (defun b/icomplete--fido-mode-setup () "Customizations to `fido-mode''s minibuffer." (when (and icomplete-mode (icomplete-simple-completing-p)) (setq-local ;; icomplete-compute-delay 0.1 ;; icomplete-hide-common-prefix t icomplete-separator " · " completion-styles '(basic substring partial-completion flex)))) (add-hook 'minibuffer-setup-hook #'b/icomplete--fido-mode-setup 1) (require 'bandali-eshell) (require 'bandali-ibuffer) ;; outline ;; (with-eval-after-load 'outline ;; (when (featurep 'which-key) ;; (which-key-add-key-based-replacements ;; "C-c @" "outline" ;; "s-O" "outline")) ;; (define-key outline-minor-mode-map (kbd "") ;; #'outline-toggle-children) ;; (define-key outline-minor-mode-map (kbd "M-p") ;; #'outline-previous-visible-heading) ;; (define-key outline-minor-mode-map (kbd "M-n") ;; #'outline-next-visible-heading) ;; (defvar b/outline-prefix-map) ;; (define-prefix-command 'b/outline-prefix-map) ;; (define-key outline-minor-mode-map (kbd "s-O") ;; 'b/outline-prefix-map) ;; (define-key b/outline-prefix-map (kbd "TAB") ;; #'outline-toggle-children) ;; (define-key b/outline-prefix-map (kbd "a") ;; #'outline-hide-body) ;; (define-key b/outline-prefix-map (kbd "H") ;; #'outline-hide-body) ;; (define-key b/outline-prefix-map (kbd "S") ;; #'outline-show-all) ;; (define-key b/outline-prefix-map (kbd "h") ;; #'outline-hide-subtree) ;; (define-key b/outline-prefix-map (kbd "s") ;; #'outline-show-subtree)) ;; (add-hook 'prog-mode-hook #'outline-minor-mode) (require 'bandali-dired) (with-eval-after-load 'help (temp-buffer-resize-mode) (csetq help-window-select t)) (with-eval-after-load 'help-mode ;; local key bindings (define-key help-mode-map (kbd "p") #'backward-button) (define-key help-mode-map (kbd "n") #'forward-button)) (with-eval-after-load 'tramp (csetq tramp-auto-save-directory (b/var "tramp/auto-save/") tramp-persistency-file-name (b/var "tramp/persistency.el")) (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" "/ssh:%h:")) (add-to-list 'tramp-default-proxies-alist '("localhost" nil nil)) (add-to-list 'tramp-default-proxies-alist (list (regexp-quote (system-name)) nil nil))) (with-eval-after-load 'doc-view (define-key doc-view-mode-map (kbd "M-RET") #'image-previous-line)) (csetq shr-max-width 80) ;; Email (with Gnus, message, and EBDB) (require 'bandali-gnus) (with-eval-after-load 'sendmail (csetq sendmail-program (executable-find "msmtp") ;; message-sendmail-extra-arguments '("-v" "-d") mail-specify-envelope-from t mail-envelope-from 'header)) (require 'bandali-message) (require 'bandali-ebdb) ;; IRC (with ERC) (require 'bandali-erc) ;; 'paste' service (aka scp + web server) (add-to-list 'load-path (b/lisp "scpaste")) (with-eval-after-load 'scpaste (csetq scpaste-http-destination "https://p.bndl.org" scpaste-scp-destination "p:~")) (autoload 'scpaste "scpaste" nil t) (autoload 'scpaste-region "scpaste" nil t) (global-set-key (kbd "C-c a p p") #'scpaste) (global-set-key (kbd "C-c a p r") #'scpaste-region) ;;; Editing ;; display Lisp objects at point in the echo area (when (version< "25" emacs-version) (with-eval-after-load 'eldoc (csetq eldoc-minor-mode-string " eldoc") (global-eldoc-mode))) ;; highlight matching parens (require 'paren) (show-paren-mode) ;; (require 'elec-pair) ;; (electric-pair-mode) (csetq ;; 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) (with-eval-after-load 'simple (column-number-mode 1) (line-number-mode 1)) ;; save minibuffer history (require 'savehist) (csetq savehist-file (b/var "savehist.el")) (savehist-mode) (add-to-list 'savehist-additional-variables 'kill-ring) ;; automatically save place in files (when (version< "25" emacs-version) (csetq save-place-file (b/var "save-place.el")) (save-place-mode)) (defun indicate-buffer-boundaries-left () (csetq indicate-buffer-boundaries 'left)) (with-eval-after-load 'prog-mode (global-prettify-symbols-mode)) (add-hook 'prog-mode-hook #'indicate-buffer-boundaries-left) (define-key text-mode-map (kbd "C-") #'b/insert-asterism) (add-hook 'text-mode-hook #'indicate-buffer-boundaries-left) (add-hook 'text-mode-hook #'flyspell-mode) (add-to-list 'auto-mode-alist '("\\.*rc$" . conf-mode)) (add-to-list 'auto-mode-alist '("\\.bashrc$" . sh-mode)) (with-eval-after-load 'flyspell (csetq flyspell-mode-line-string " fly")) ;; flycheck ;; (run-with-idle-timer 0.6 nil #'require 'flycheck) ;; (with-eval-after-load 'flycheck ;; (csetq ;; ;; Use the load-path from running Emacs when checking elisp files ;; flycheck-emacs-lisp-load-path 'inherit ;; ;; Only flycheck when I actually save the buffer ;; flycheck-check-syntax-automatically '(mode-enabled save) ;; flycheck-mode-line-prefix "flyc")) ;; (define-key flycheck-mode-map (kbd "M-P") #'flycheck-previous-error) ;; (define-key flycheck-mode-map (kbd "M-N") #'flycheck-next-error) ;; (add-hook 'prog-mode-hook #'flycheck-mode) ;; ispell ;; http://endlessparentheses.com/ispell-and-apostrophes.html ;; (run-with-idle-timer 0.6 nil #'require 'ispell) ;; (with-eval-after-load 'ispell ;; ;; ’ can be part of a word ;; (csetq ispell-local-dictionary-alist ;; `((nil "[[:alpha:]]" "[^[:alpha:]]" ;; "['\x2019]" nil ("-B") nil utf-8)) ;; ispell-program-name (executable-find "hunspell")) ;; ;; don't send ’ to the subprocess ;; (defun endless/replace-apostrophe (args) ;; (cons (replace-regexp-in-string ;; "’" "'" (car args)) ;; (cdr args))) ;; (advice-add #'ispell-send-string :filter-args ;; #'endless/replace-apostrophe) ;; ;; convert ' back to ’ from the subprocess ;; (defun endless/replace-quote (args) ;; (if (not (derived-mode-p 'org-mode)) ;; args ;; (cons (replace-regexp-in-string ;; "'" "’" (car args)) ;; (cdr args)))) ;; (advice-add #'ispell-parse-output :filter-args ;; #'endless/replace-quote)) ;; abbrev (csetq abbrev-file-name (b/etc "abbrev.el")) (add-hook 'text-mode-hook #'abbrev-mode) ;;; Programming modes (with-eval-after-load 'lisp-mode (defun indent-spaces-mode () (setq indent-tabs-mode nil)) (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode)) ;; alloy (add-to-list 'load-path (b/lisp "alloy-mode")) (autoload 'alloy-mode "alloy-mode" nil t) (with-eval-after-load 'alloy-mode (csetq alloy-basic-offset 2) ;; (defun b/alloy-simple-indent (start end) ;; (interactive "r") ;; ;; (if (region-active-p) ;; ;; (indent-rigidly start end alloy-basic-offset) ;; ;; (if (bolp) ;; ;; (indent-rigidly (line-beginning-position) ;; ;; (line-end-position) ;; ;; alloy-basic-offset))) ;; (indent-to (+ (current-column) alloy-basic-offset))) ;; local key bindings (define-key alloy-mode-map (kbd "RET") #'electric-newline-and-maybe-indent) ;; (define-key alloy-mode-map (kbd "TAB") #'b/alloy-simple-indent) (define-key alloy-mode-map (kbd "TAB") #'indent-for-tab-command)) (add-to-list 'auto-mode-alist '("\\.\\(als\\|dsh\\)\\'" . alloy-mode)) (add-hook 'alloy-mode-hook (lambda nil (setq-local indent-tabs-mode nil))) ;; lean ;; (eval-when-compile (defvar lean-mode-map)) ;; (run-with-idle-timer 0.4 nil #'require 'lean-mode) ;; (with-eval-after-load 'lean-mode ;; (require 'lean-input) ;; (csetq default-input-method "Lean" ;; lean-input-tweak-all '(lean-input-compose ;; (lean-input-prepend "/") ;; (lean-input-nonempty)) ;; lean-input-user-translations '(("/" "/"))) ;; (lean-input-setup) ;; ;; local key bindings ;; (define-key lean-mode-map (kbd "S-SPC") #'company-complete)) (with-eval-after-load 'sgml-mode (csetq sgml-basic-offset 0)) (with-eval-after-load 'css-mode (csetq css-indent-offset 2)) ;; po-mode ;; (add-hook 'po-mode-hook (lambda nil (run-with-timer 0.1 nil 'View-exit))) ;; auctex ;; (csetq font-latex-fontify-sectioning 'color) (with-eval-after-load 'tex-mode (cl-delete-if (lambda (p) (string-match "^---?" (car p))) tex--prettify-symbols-alist)) (add-hook 'tex-mode-hook #'auto-fill-mode) (add-hook 'tex-mode-hook #'flyspell-mode) ;;; Emacs enhancements & auxiliary packages (with-eval-after-load 'man (csetq Man-width 80)) (defun b/*scratch* () "Switch to `*scratch*' buffer, creating it if it does not exist." (interactive) (switch-to-buffer (or (get-buffer "*scratch*") (with-current-buffer (get-buffer-create "*scratch*") (set-buffer-major-mode (current-buffer)) (current-buffer))))) (global-set-key (kbd "C-c s") #'b/*scratch*) ;; ,---- ;; | make pretty boxed quotes like this ;; `---- (add-to-list 'load-path (b/lisp "boxquote")) (run-with-idle-timer 0.6 nil #'require 'boxquote) (with-eval-after-load 'boxquote (defvar b/boxquote-prefix-map) (define-prefix-command 'b/boxquote-prefix-map) (global-set-key (kbd "C-c q") 'b/boxquote-prefix-map) (define-key b/boxquote-prefix-map (kbd "b") #'boxquote-buffer) (define-key b/boxquote-prefix-map (kbd "B") #'boxquote-insert-buffer) (define-key b/boxquote-prefix-map (kbd "d") #'boxquote-defun) (define-key b/boxquote-prefix-map (kbd "F") #'boxquote-insert-file) (define-key b/boxquote-prefix-map (kbd "hf") #'boxquote-describe-function) (define-key b/boxquote-prefix-map (kbd "hk") #'boxquote-describe-key) (define-key b/boxquote-prefix-map (kbd "hv") #'boxquote-describe-variable) (define-key b/boxquote-prefix-map (kbd "hw") #'boxquote-where-is) (define-key b/boxquote-prefix-map (kbd "k") #'boxquote-kill) (define-key b/boxquote-prefix-map (kbd "p") #'boxquote-paragraph) (define-key b/boxquote-prefix-map (kbd "q") #'boxquote-boxquote) (define-key b/boxquote-prefix-map (kbd "r") #'boxquote-region) (define-key b/boxquote-prefix-map (kbd "s") #'boxquote-shell-command) (define-key b/boxquote-prefix-map (kbd "t") #'boxquote-text) (define-key b/boxquote-prefix-map (kbd "T") #'boxquote-title) (define-key b/boxquote-prefix-map (kbd "u") #'boxquote-unbox) (define-key b/boxquote-prefix-map (kbd "U") #'boxquote-unbox-region) (define-key b/boxquote-prefix-map (kbd "y") #'boxquote-yank) (define-key b/boxquote-prefix-map (kbd "M-q") #'boxquote-fill-paragraph) (define-key b/boxquote-prefix-map (kbd "M-w") #'boxquote-kill-ring-save)) (add-to-list 'load-path (b/lisp "hl-todo")) (run-with-idle-timer 0.5 nil #'require 'hl-todo) (with-eval-after-load 'hl-todo ;; highlight TODOs in buffers (global-hl-todo-mode)) ;; expand-region (global-set-key (kbd "C-=") #'er/expand-region) (run-with-idle-timer 0.6 nil #'require 'yasnippet) (with-eval-after-load 'yasnippet (declare-function yas-reload-all "yasnippet" (&optional no-jit interactive)) (declare-function yas-maybe-expand-abbrev-key-filter "yasnippet" (cmd)) (defconst yas-verbosity-cur yas-verbosity) (setq yas-verbosity 2) (csetq yas-snippet-dirs `(,(b/etc "yasnippet/snippets"))) ;; (add-to-list 'yas-snippet-dirs "~/src/git/guix/etc/snippets" t) (yas-reload-all) (setq yas-verbosity yas-verbosity-cur) (defun b/yas-maybe-expand-abbrev-key-filter (cmd) (when (and (yas-maybe-expand-abbrev-key-filter cmd) (not (bound-and-true-p git-commit-mode))) cmd)) (defconst b/yas-maybe-expand '(menu-item "" yas-expand :filter b/yas-maybe-expand-abbrev-key-filter)) (define-key yas-minor-mode-map (kbd "SPC") b/yas-maybe-expand) (yas-global-mode)) ;; debbugs (global-set-key (kbd "C-c D d") #'debbugs-gnu) (global-set-key (kbd "C-c D b") #'debbugs-gnu-bugs) (global-set-key (kbd "C-c D e") ; bug-gnu-emacs (lambda () (interactive) (setq debbugs-gnu-current-suppress t) (debbugs-gnu debbugs-gnu-default-severities '("emacs")))) (global-set-key (kbd "C-c D g") ; bug-gnuzilla (lambda () (interactive) (setq debbugs-gnu-current-suppress t) (debbugs-gnu debbugs-gnu-default-severities '("gnuzilla")))) ;; url and url-cache (csetq url-configuration-directory (b/var "url/configuration/") url-cache-directory (b/var "url/cache/")) ;; eww (csetq eww-download-directory (file-name-as-directory (getenv "XDG_DOWNLOAD_DIR"))) (global-set-key (kbd "C-c a e w") #'eww) ;; ;; org-ref ;; (csetq ;; reftex-default-bibliography '("~/usr/org/references.bib") ;; org-ref-default-bibliography '("~/usr/org/references.bib") ;; org-ref-bibliography-notes "~/usr/org/notes.org" ;; org-ref-pdf-directory "~/usr/org/bibtex-pdfs/") ;; fill-column-indicator ? ;; window (csetq split-width-threshold 150) (global-set-key (kbd "C-c w s l") (lambda () (interactive) (split-window-right) (other-window 1))) (global-set-key (kbd "C-c w s j") (lambda () (interactive) (split-window-below) (other-window 1))) (global-set-key (kbd "C-c w q") #'quit-window) ;; pass ;; (global-set-key (kbd "C-c a p") #'pass) ;; (add-hook 'pass-mode-hook #'View-exit) ;; reftex ;; uncomment to disable reftex-cite's default choice of previous word ;; (with-eval-after-load 'reftex ;; (require 'reftex-cite) ;; (defun reftex-get-bibkey-default () ;; "If the cursor is in a citation macro, return the word before the macro." ;; (let* ((macro (reftex-what-macro 1))) ;; (save-excursion ;; (when (and macro (string-match "cite" (car macro))) ;; (goto-char (cdr macro))) ;; (reftex-this-word))))) (add-hook 'latex-mode-hook #'reftex-mode) ;; dmenu (add-to-list 'load-path (b/lisp "dmenu")) (with-eval-after-load 'dmenu (csetq dmenu-prompt-string "run: " dmenu-save-file (b/var "dmenu-items"))) (autoload 'dmenu "dmenu" nil t) ;; eosd ? ;; delight (run-with-idle-timer 0.5 nil #'require 'delight) (with-eval-after-load 'delight (delight 'auto-fill-function " f" "simple") (delight 'abbrev-mode "" "abbrev") (delight 'mml-mode " mml" "mml") (delight 'yas-minor-mode "" "yasnippet")) ;;; Post initialization (message "Loading %s...done (%.3fs)" user-init-file (float-time (time-subtract (current-time) b/before-user-init-time))) ;;; init.el ends here