;;; ~/.emacs.d/early-init.el -*- lexical-binding: t; -*- ;; Author: Case Duckworth (setopt frame-inhibit-implied-resize t) (setopt frame-resize-pixelwise t) (setopt window-resize-pixelwise t) (setopt default-frame-alist '((menu-bar-lines . 0) (tool-bar-lines . 0) (vertical-scroll-bars) (horizontal-scroll-bars))) (defvar *fonts* '((default :family ;;("Recursive Mono Casual Static" "DejaVu Sans Mono") ("Public Sans" "DejaVu Sans") :height 100) (variable-pitch :family ("Public Sans" "DejaVu Sans") :height 1.0) (fixed-pitch :family ("Recursive Mono Casual Static" "DejaVu Sans Mono")) (fixed-pitch-serif :family ("Recursive Mono Linear Static" "DejaVu Sans Mono")))) (require 'package) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/")) (package-initialize) ;;; Custom functions (defun pulse@eval (start end &rest _) "ADVICE: makes `pulse-momentary-highlight-region' accept other arities." (pulse-momentary-highlight-region start end)) (defun create-missing-directories () "Automatically create missing directories." (let ((target-dir (file-name-directory buffer-file-name))) (unless (file-exists-p target-dir) (make-directory target-dir :parents)))) (defun delete-trailing-whitespace-except-current-line () "Delete all trailing whitespace except current line." (save-excursion (delete-trailing-whitespace (point-min) (line-beginning-position)) (delete-trailing-whitespace (line-end-position) (point-max)))) (defun run-after-frame-init (func) "Run FUNC after the first frame is initialized. If already so, run FUNC immediately." (cond ((daemonp) (add-hook 'server-after-make-frame-hook func) (advice-add func :after (lambda () (remove-hook 'server-after-make-frame-hook func) (advice-remove func 'after-frame-init-removing-advice)) '((name . after-frame-init-removing-advice)))) ((not after-init-time) (add-hook 'after-init-hook func)) (:else (funcall func)))) (defun first-found-font (&rest cands) "Return the first font of CANDS that is installed, or nil." (cl-loop with ffl = (font-family-list) for font in cands if (member font ffl) return font)) (defun setup-faces () "Setup Emacs faces." ;; Default faces (cl-loop for (face . spec) in *fonts* do (set-face-attribute face nil :family (apply #'first-found-font (plist-get spec :family)) :height (or (plist-get spec :height) 'unspecified))) ;; Specialized fonts (cl-loop with ffl = (font-family-list) for (charset . font) in '((latin . "Noto Sans") (han . "Noto Sans CJK SC Regular") (kana . "Noto Sans CJK JP Regular") (hangul . "Noto Sans CJK KR Regular") (cjk-misc . "Noto Sans CJK KR Regular") (khmer . "Noto Sans Khmer") (lao . "Noto Sans Lao") (burmese . "Noto Sans Myanmar") (thai . "Noto Sans Thai") (ethiopic . "Noto Sans Ethiopic") (hebrew . "Noto Sans Hebrew") (arabic . "Noto Sans Arabic") (gujarati . "Noto Sans Gujarati") (devanagari . "Noto Sans Devanagari") (kannada . "Noto Sans Kannada") (malayalam . "Noto Sans Malayalam") (oriya . "Noto Sans Oriya") (sinhala . "Noto Sans Sinhala") (tamil . "Noto Sans Tamil") (telugu . "Noto Sans Telugu") (tibetan . "Noto Sans Tibetan") ;; emojis (symbol . "Noto Emoji") (symbol . "Noto Color Emoji") (symbol . "Segoe UI Emoji") (symbol . "Apple Color Emoji") (symbol . "FreeSans") (symbol . "FreeMono") (symbol . "FreeSerif") (symbol . "Unifont") (symbol . "Symbola")) if (member font ffl) do (set-fontset-font t charset font))) (defmacro inhibit-messages (&rest body) "Inhibit all messages in BODY." (declare (indent defun)) `(cl-letf (((symbol-function 'message) #'ignore)) ,@body)) (defun kill-buffer-dwim (&optional buffer-or-name) "Kill BUFFER-OR-NAME or the current buffer." (interactive "P") (cond ((bufferp buffer-or-name) (kill-buffer buffer-or-name)) ((null buffer-or-name) (kill-current-buffer)) (:else (kill-buffer (read-buffer "Kill: " nil :require-match))))) (defun other-window-dwim (&optional arg) "Switch to another window/buffer. Calls `other-window', which see, unless - the current window is alone on its frame - `other-window-dwim' is called with \\[universal-argument] In these cases, switch to the last-used buffer." (interactive "P") (if (or arg (one-window-p)) (switch-to-buffer (other-buffer) nil t) (other-window 1))) (defun delete-window-dwim () "Delete the current window or bury its buffer. If the current window is alone in its frame, bury the buffer instead." (interactive) (unless (ignore-errors (delete-window) t) (bury-buffer))) (defun cycle-spacing* (&optional n) "Negate N argument on `cycle-spacing'." (interactive "*p") (cycle-spacing (- n))) (defmacro find-user-file (name &optional file-name) "Template macro to generate user file finding functions." (declare (indent 1)) (let ((file-name (or file-name (intern (format "user-%s-file" name)))) (func-name (intern (format "find-user-%s-file" name)))) `(defun ,func-name (&optional arg) ,(format "Edit `%s' in the current window. With ARG, edit in the other window." file-name) (interactive "P") (funcall (if arg #'find-file-other-window #'find-file) ,file-name)))) (defun indent-buffer+ () "Indent the current buffer and (un)`tabify'. Whether it tabifies or untabifies depends on `space-indent-modes'." (interactive) (save-mark-and-excursion (indent-region (point-min) (point-max)) (if (apply #'derived-mode-p space-indent-modes) (untabify (point-min) (point-max)) (tabify (point-min) (point-max))))) (defun package-ensure (pkg) "Install PKG if it's not already installed." (unless (package-installed-p pkg) (package-vc-install pkg))) (defun minibuffer-delete-directory () "Delete the last directory in a file-completing minibuffer." (interactive) (let ((here (point)) (meta (completion-metadata "" minibuffer-completion-table minibuffer-completion-predicate))) (if (eq (completion-metadata-get meta 'category) 'file) (when (search-backward "/" (minibuffer-prompt-end) t) (delete-region (point) here)) (backward-kill-word 1)))) (defun save-buffers-kill* (arg) "Save all the buffers and kill ... something. If ARG is 1 (called normally), kill the current terminal. If ARG is 4 (with C-u), kill emacs but ask if there are processes running. If ARG is 16, kill emacs without asking about processes." (interactive "p") (pcase arg (1 (save-buffers-kill-terminal)) (4 (save-buffers-kill-emacs t)) (16 (let ((confirm-kill-processes nil) (kill-emacs-query-functions nil) (confirm-kill-emacs nil)) (save-buffers-kill-emacs t))))) (defun regexp-concat (&rest regexps) (string-join regexps "\\|"))