;;; basics.el --- Super basic Emacs settings -*- lexical-binding: t -*- ;;; Commentary: ;; These are the settings that I literally cannot live without. Basic ;; settings, built-in packages, that kind of stuff. Everything else ;; goes in init.el. ;;; Code: ;;; Directories (defmacro defdir (name directory &optional docstring makedir) "Define a variable and a function NAME expanding to DIRECTORY. DOCSTRING is applied to the variable; its default is DIRECTORY's path. If MAKEDIR is non-nil, the directory and its parents will be created." (declare (indent 2) (doc-string 3)) `(progn (defvar ,name (expand-file-name ,directory) ,(concat (or docstring (format "%s" directory)) "\n" "Defined by `defdir'.")) (defun ,name (file &optional mkdir) ,(concat "Expand FILE relative to variable `" (symbol-name name) "'.\n" "If MKDIR is non-nil, parent directories are created.\n" "Defined by `defdir'.") (let ((file-name (expand-file-name (convert-standard-filename file) ,name))) (when mkdir (make-directory (file-name-directory file-name) :parents)) file-name)) ,(if makedir `(make-directory ,directory :parents) `(unless (file-exists-p ,directory) (warn "Directory `%s' doesn't exist." ,directory))))) (defdir etc/ (locate-user-emacs-file "etc/") "Where various Emacs files are placed." :makedir) (defdir lisp/ (locate-user-emacs-file "lisp/") "My bespoke elisp files." :makedir) (push lisp/ load-path) (defdir sync/ "~/Sync/" "My Syncthing directory." :makedir) (defdir private/ (sync/ "emacs/private/") "Private files and stuff." :makedir) (push private/ load-path) (use-package no-littering :ensure t :demand t :preface (setq-default no-littering-etc-directory etc/ no-littering-var-directory etc/)) ;;; Settings ;; Async (setq-default async-shell-command-buffer 'new-buffer async-shell-command-display-buffer nil) ;; Scrolling (setq-default auto-hscroll-mode t auto-window-vscroll nil fast-but-imprecise-scrolling t hscroll-margin 1 hscroll-step 1 scroll-conservatively 25 scroll-margin 0 scroll-preserve-screen-position 1 scroll-step 1) (scroll-bar-mode -1) (horizontal-scroll-bar-mode -1) (pixel-scroll-precision-mode) ;; Cursor (setq-default cursor-in-non-selected-windows 'hollow cursor-type 'bar blink-cursor-blinks 1 blink-cursor-interval 0.25 blink-cursor-delay 0.25) (blink-cursor-mode) ;; Mouse (setq-default mouse-drag-copy-region t mouse-wheel-progressive-speed nil mouse-yank-at-point t) ;; Dialogs (unless (boundp 'use-short-answers) (fset 'yes-or-no-p 'y-or-n-p)) (setq-default read-answer-short t use-dialog-box nil use-file-dialog nil use-short-answers t) ;; Minibuffer (setq-default completion-ignore-case t read-buffer-completion-ignore-case t read-file-name-completion-ignore-case t completions-detailed t enable-recursive-minibuffers t file-name-shadow-properties '(invisible t intangible t) minibuffer-eldef-shorten-default t minibuffer-prompt-properties '( read-only t cursor-intangible t face minibuffer-prompt)) (file-name-shadow-mode) (minibuffer-electric-default-mode) (require 'savehist) (setq-default history-length 1024 history-delete-duplicates t ;; savehist-file (etc/ "savehist.el") savehist-save-minibuffer-history t savehist-autosave-interval 30) (savehist-mode) ;; Undo (setq-default undo-limit (* 10 1024 1024)) ;; Killing and yanking (setq-default kill-do-not-save-duplicates t kill-read-only-ok t save-interprogram-paste-before-kill t yank-pop-change-selection t) (delete-selection-mode) ;; Notifying the user (setq-default echo-keystrokes 0.01 ring-bell-function #'ignore) ;; Point and mark (setq-default set-mark-command-repeat-pop t) ;; The system (setq-default read-process-output-max (* 10 1024 1024)) ;; Startup (setq-default inhibit-startup-screen t initial-buffer-choice t initial-scratch-message nil) ;; (menu-bar-mode -1) (tool-bar-mode -1) (tooltip-mode -1) ;; Text editing (setq-default fill-column 80 sentence-end-double-space t tab-width 8) (global-so-long-mode) (setq-default show-paren-delay 0.01 show-paren-style 'parenthesis show-paren-when-point-in-periphery t show-paren-when-point-inside-paren t) (show-paren-mode) (electric-pair-mode) ;; Encodings (set-language-environment "UTF-8") (setq-default buffer-file-coding-system 'utf-8-unix coding-system-for-read 'utf-8-unix coding-system-for-write 'utf-8-unix default-process-coding-system '(utf-8-unix . utf-8-unix) locale-coding-system 'utf-8-unix) (set-charset-priority 'unicode) (prefer-coding-system 'utf-8-unix) (set-default-coding-systems 'utf-8-unix) (set-terminal-coding-system 'utf-8-unix) (set-keyboard-coding-system 'utf-8-unix) (pcase system-type ((or 'ms-dos 'windows-nt) (set-clipboard-coding-system 'utf-16-le) (set-selection-coding-system 'utf-16-le)) (_ (set-selection-coding-system 'utf-8) (set-clipboard-coding-system 'utf-8))) ;; Abbrev (setq-default abbrev-file-name (sync/ "abbrev.el") save-abbrevs 'silently) ;; Files (setq-default auto-revert-verbose nil global-auto-revert-non-file-buffers t create-lockfiles nil find-file-visit-truename t mode-require-final-newline t view-read-only t save-silently t) (global-auto-revert-mode) (setq-default auto-save-default nil auto-save-interval 1 auto-save-no-message t auto-save-timeout 1 auto-save-visited-interval 1) (add-to-list 'auto-save-file-name-transforms `(".*" ,(etc/ "auto-save/" t) t)) (auto-save-visited-mode) (setq-default backup-by-copying t version-control t kept-new-versions 8 kept-old-versions 8 delete-old-versions t) (require 'recentf) (setq-default ;; recentf-save-file (etc/ "recentf" t) recentf-max-menu-items 500 recentf-max-saved-items nil ; Save the whole list recentf-auto-cleanup 'mode) (add-to-list 'recentf-exclude etc/) (add-to-list 'recentf-exclude "-autoloads.el\\'") (add-hook 'buffer-list-update-hook #'recentf-track-opened-file) (recentf-mode) (require 'saveplace) (setq-default ;; save-place-file (etc/ "places.el") save-place-forget-unreadable-files (eq system-type 'gnu/linux)) (save-place-mode) (require 'uniquify) (setq uniquify-after-kill-buffer-p t uniquify-buffer-name-style 'forward uniquify-ignore-buffers-re "^\\*" uniquify-separator path-separator) (setq-local vc-follow-symlinks t vc-make-backup-files t) ;; Native compilation (setq-default native-comp-async-report-warnings-errors 'silent native-comp-deferred-compilation t native-compile-target-directory (etc/ "eln" t)) (add-to-list 'native-comp-eln-load-path native-compile-target-directory) (when (fboundp 'startup-redirect-eln-cache) (startup-redirect-eln-cache native-compile-target-directory)) ;; Custom file (setq-default custom-file (sync/ "emacs/custom.el")) (define-advice package--save-selected-packages (:around (orig &rest args) no-custom) "Don't save `package-selected-packages' to `custom-file'." (let ((custom-file null-device)) (apply orig args))) ;; Goto Address (if (fboundp 'global-goto-address-mode) (global-goto-address-mode) (add-hook 'after-change-major-mode-hook #'goto-address-mode)) ;; Winner (winner-mode) ;;; Keybindings (defun other-window|switch-buffer (arg) "Call `other-window' or `switch-buffer' depending on windows. When called with prefix ARG, unconditionally switch buffer." (interactive "P") (if (or arg (one-window-p)) (switch-to-buffer (other-buffer) nil t) (other-window 1))) (defun delete-window|bury-buffer () "Delete the current window, or bury the current buffer. If the current window is the only window, bury the buffer." (interactive) (condition-case e (delete-window) (t (bury-buffer)))) (defun +cycle-spacing (&optional n) ;; `cycle-spacing' is wildly different in 29.1 over 28. "Negate N argument on `cycle-spacing'. That is, with a positive N, deletes newlines as well, leaving -N spaces. If N is negative, it will not delete newlines and leave N spaces." (interactive "*p") (cycle-spacing (- n))) (global-set-key [remap eval-expression] #'pp-eval-expression) (global-set-key (kbd "M-o") #'other-window|switch-buffer) (global-set-key (kbd "C-x 0") #'delete-window|bury-buffer) (global-set-key (kbd "M-SPC") #'+cycle-spacing) (global-set-key (kbd "C-x C-k") #'kill-this-buffer) ;;; Hooks (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p) (add-hook 'find-file-not-found-functions (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))))) (add-hook 'find-file-hook (defun vc-remote-off () "Turn VC off when remote." (when (file-remote-p (buffer-file-name)) (setq-local vc-handled-backends nil)))) ;;; Advice (define-advice switch-to-buffer (:after (&rest _) normal-mode) "Automatically determine the mode for non-file buffers." (when-let ((_ (and (eq major-mode 'fundamental-mode))) (buffer-file-name (buffer-name))) (normal-mode))) (define-advice canonically-space-region (:around (orig &rest args) double-space-sentences) "Always double-space sentences canonically." (let ((sentence-end-double-space t)) (apply orig args))) ;; With region or ... (defun advise-region-or-buffer (&rest _) "`:before' advice to work on the active region or whole buffer. See also `with-region-or-buffer'." (interactive (if mark-active (list (region-beginning) (region-end)) (list (point-min) (point-max))))) (defun advise-region-or-line (&rest _) "`:before' advice to work on the active region or whole line. See also `with-region-or-line'." (interactive (if mark-active (list (region-beginning) (region-end)) (list (line-beginning-position) (line-end-position))))) (defun advise-region-or-to-eol (&rest _) "`:before' advice to work on the active region or to end of line. See also `with-region-or-to-eol'." (INTERACTIVE (if mark-active (list (region-beginning) (region-end)) (list (point) (line-end-position))))) (defmacro with-region-or-buffer (&rest funcs) "Advise FUNCS with `advise-region-or-buffer'." `(progn ,@(cl-loop for fn in funcs collect `(advice-add ',fn :before #'advise-region-or-buffer)))) (defmacro with-region-or-line (&rest funcs) "Advise FUNCS with `advise-region-or-line'." `(progn ,@(cl-loop for fn in funcs collect `(advice-add ',fn :before #'advise-region-or-line)))) (defmacro with-region-or-to-eol (&rest funcs) "Advise FUNCS with `advise-region-or-to-eol'." `(progn ,@(cl-loop for fn in funcs collect `(advice-add ',fn :before #'advise-region-or-to-eol)))) (with-region-or-buffer indent-region) ;;; Packages (use-package _acdw :load-path private/) ;;; basics.el ends here