;; -*- lexical-binding: t; -*- ;;; Use UTF-8 by default (set-language-environment "UTF-8") ;;; No iconofications (define-key global-map (vector 'remap 'iconify-frame) #'intentionally-disabled-bind) ;;; No limit in printing ;; I oft want to print the results of evaluating expressions. (set 'eval-expression-print-length nil) ;; A value of nil means no limit. (set 'eval-expression-print-level nil) ;; A value of nil means no limit. ;; useful setting for preserving the system clipboards (setq save-interprogram-paste-before-kill t) ;;; Two spaces after the end of sentences ;; This marks the boundaries of sentences clearer. (setq sentence-end-double-space t) ;;; Setting the direction of the text can speed things up (set-default 'bidi-paragraph-direction 'left-to-right) (set 'bidi-inhibit-bpa t) ;;; resize horizontally (set 'fit-window-to-buffer-horizontally t) ;;; Important in order to read passwords (setq epg-pinentry-mode 'loopback) ;;; disable some default modes ;; This is just my personal preference: I prefer not to have the top ;; of my Emacs show anything other than the buffer itself. (tool-bar-mode -1) (menu-bar-mode -1) (scroll-bar-mode -1) (blink-cursor-mode -1) ;;; Say y instead of yes (fset 'yes-or-no-p 'y-or-n-p) ;;; Recursive minibuffers is almost indispensable for me (set 'enable-recursive-minibuffers t) ;;; Use spaces instead of tabs (set-default 'indent-tabs-mode nil) ;;; Don't make noise when saving files (setq save-silently t) ;;; no title on the frame (setq frame-title-format "" icon-title-format "") ;;; major mode of the scratch buffer (set 'initial-major-mode 'emacs-lisp-mode) ;;; echo quickly (setq echo-keystrokes 0.002) ;;; Scroll conservatively please (setq scroll-conservatively 30) ;;; remember my minibuffer (require 'savehist) (set 'savehist-file (expand-file-name "savehist" load-file-directory)) (set 'history-length 1024) (set 'history-delete-duplicates t) (set 'savehist-save-minibuffer-history t) (savehist-mode) ;;; a large file threshold (set 'large-file-warning-threshold (* 1024 1024 1024)) ;;; don't use a GUI dialog box as that is distracting to me (setq use-dialog-box nil) ;;; don't make noise ;; and when two buffers have the same base name, include more parts to distinguish them (setq uniquify-buffer-name-style 'forward ring-bell-function #'ignore visible-bell nil) ;;; mac specific settings (setq ns-right-alternate-modifier 'none) (setq ns-function-modifier (list :ordinary 'hyper :function 'none :mouse 'none)) (setq ns-pop-up-frames nil) (setq ns-use-native-fullscreen nil) (setq ns-use-proxy-icon nil) ;;; set info directory (eval-after-load "info" '(add-to-list 'Info-directory-list "/Users/durand/w.emacs.d/emacs/info/")) ;;; bookmark (define-key global-map (vector ?\H-b) #'bookmark-jump) (define-key global-map (vector ?\H-m) #'bookmark-set) ;; This colorized output is annoying to me. (setq bookmark-fontify nil) ;;; Use entire map for projects (require 'project) (setq project-switch-use-entire-map t) ;;; eshell (define-key global-map (vector ?\H-e) #'eshell) ;;; Find function on key ;; I think it is a useful addition to the help system to bind this to ;; the help map. (define-key help-map (vector ?\M-f) #'find-function-on-key) ;;; bury-buffer (define-key global-map (vector ?\C-\s-b) #'bury-buffer) (define-key global-map (vector ?\H-q) #'bury-buffer) ;;; Repeat (define-key global-map (vector ?\s-z) #'repeat) ;;; where to find the C source code of Emacs. (setq find-function-C-source-directory "/Users/durand/w.emacs.d/emacs/src/") ;;; frame parameters (setq initial-frame-alist '((width . 118))) (set-frame-width nil 118) (add-to-list 'default-frame-alist '(width . 118)) (add-to-list 'default-frame-alist '(height . 35)) (add-to-list 'default-frame-alist '(font . "Droid Sans Mono for Powerline-20")) (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t)) (add-to-list 'default-frame-alist '(ns-appearance . dark)) (add-to-list 'default-frame-alist '(fullscreen . maximized)) (setq frame-resize-pixelwise t) (setq revert-without-query '(".*")) (set-face-attribute 'variable-pitch nil :family "Avenir" :height 1.0) ;;; Adjust default size ;;;###autoload (defvar durand-frame-width-pixel 1420 "Default frame width in pixels. Set the frame to this width in order to fill my screen.") ;;;###autoload (defvar durand-frame-height-pixel 842 "Default frame height in pixels. Set the frame to this height in order to fill my screen.") ;;;###autoload (defun durand-adjust-font-size (delta) "Increase the default font size by DELTA. If DELTA is negative, decrease the size by (- DELTA). This will maintain the frame's width and height as well." (let* ((width durand-frame-width-pixel) (height durand-frame-height-pixel) (current-font (face-attribute 'default :font)) (font-name (aref (query-font current-font) 0)) (current-height (progn (string-match "[[:alpha:]-*]+\\([[:digit:]]+\\)" font-name) (string-to-number (match-string 1 font-name)))) (new-name (replace-match (number-to-string (+ delta current-height)) nil nil font-name 1))) (set-face-attribute 'default nil :font new-name) (set-frame-width (selected-frame) width nil t) (set-frame-height (selected-frame) height nil t))) (durand-hide-minor-mode buffer-face-mode face-remap " BF") ;;; disable line numbers, as that is a performace killer for me. (setq-default display-line-numbers-type nil) (global-display-line-numbers-mode -1) ;;; display-buffer-alist (require 'rx) (setq display-buffer-alist `((,(rx (seq bos "*Help*" eos)) (display-buffer-in-side-window) (side . bottom) (slot . 1) (window-height . 0.37)) (,(rx (seq bos "*Messages*" eos)) (display-buffer-in-side-window) (side . bottom) (slot . -1) (window-height . 0.37)) (,(rx (seq bos "magit: ")) (display-buffer-same-window)) (,(rx (seq bos "*Group*" eos)) (display-buffer-in-tab durand-display-in-one-window) (tab-name . "email")) (,(rx (seq bos "*Flymake")) (display-buffer-at-bottom) (window-height . 0.2)) (,(rx (seq bos "*Man " (one-or-more not-newline) "*" eos)) (display-buffer-in-tab durand-display-in-one-window) (tab-name . "man page")))) ;;; copy-duplicate dwim (define-key global-map (vector ?\s-\;) #'durand-copy-line-dwim) ;;; Custom kill buffer function ;;;###autoload (defun durand-kill-current-buffer (&optional arg) "Kill the current buffer. If the optional ARG is non-nil, and if there are more than one window, then also delete the selected window." (interactive "P") (cond ((window-minibuffer-p (selected-window)) ;; this is the same as calling `abort-recursive-edit'. (throw 'exit t)) ((derived-mode-p 'pdf-view-mode) ;; We also want to kill the associated timer, if any. (mapc (lambda (timer) (cond ((and (eq (timer--function timer) 'pdf-cache--prefetch-start) (listp (timer--args timer)) (memq (current-buffer) (timer--args timer))) (setq timer-idle-list (delq timer timer-idle-list))))) timer-idle-list) (mapc (lambda (timer) (cond ((and (eq (timer--function timer) 'pdf-cache--prefetch-start) (listp (timer--args timer)) (memq (current-buffer) (timer--args timer))) (setq timer-list (delq timer timer-list))))) timer-list) (kill-buffer (current-buffer))) (t (kill-buffer (current-buffer)))) (cond ((and arg (not (one-window-p t))) (delete-window (selected-window))))) (define-key global-map (vector ?\s-k) #'durand-kill-current-buffer) ;;; zap-up-to-char and zap-to-char are both useful. (define-key global-map (vector (logior (ash 1 27) #x5a)) #'zap-up-to-char) ;;; Completion enhanced yank (define-key global-map (vector ?\M-y) #'yank-pop) (define-key global-map (vector ?\C-\M-y) #'yank-complete) ;;; disable C-z. That has been constantly annoying. (define-key global-map (vector ?\C-z) #'intentionally-disabled-bind) ;;; Repeating pops ;;;###autoload (setq set-mark-command-repeat-pop t) ;;; I prefer going to the top first. ;; But I don't really like always going to the top. I am still ;; thinking about solutions. -- 2021-01-17 ;; REVIEW: I am still thinking about solutions... -- 2022-01-15 ;; 00:29:48.391643 (setq recenter-positions (list 'middle 'top 'bottom)) ;;; Pulse the current line ;;;###autoload (defface prot-pulse-line '((default :extend t) (((class color) (min-colors 88) (background light)) :background "#8eecf4") (((class color) (min-colors 88) (background dark)) :background "#004065") (t :inverse-video t)) "Default face for `durand-pulse-pulse-line'. I stole from Protesilaos' dotemacs.") (require 'pulse) ;;;###autoload (defun durand-pulse-pulse-line (&optional face) "Temporarily highlight the current line with optional FACE." (interactive) (let ((start (cond ((= (point) (point-max)) (save-excursion (forward-line -1) (point))) ((save-excursion (forward-line 0) (point))))) (end (save-excursion (forward-line 1) (point))) (pulse-delay .04) (face (or face 'prot-pulse-line))) (pulse-momentary-highlight-region start end face))) (define-key global-map (vector 's-escape) #'durand-pulse-pulse-line) ;;;###autoload (defun durand-pulse-recenter-top () "Recenter to the top and pulse the line." (interactive) (recenter 0) (durand-pulse-pulse-line)) ;;; auto-fill for texts (set 'adaptive-fill-mode t) (add-hook 'text-mode-hook #'auto-fill-mode) ;;; Hide auto-fill mode in the mode line. ;; The original value is " Fill" (durand-hide-minor-mode auto-fill-function) ;;; Hide auto-revert-mode ;; The original is auto-revert-mode-text (durand-hide-minor-mode auto-revert-mode autorevert) ;;; enable all commands (set 'disabled-command-function nil) ;;; Comments relateed (require 'newcomment) (set 'comment-empty-lines nil) (set 'comment-fill-column nil) (set 'comment-multi-line t) (set 'comment-style 'multi-line) ;;; Toggle line numbers (define-key global-map (vector 'f9) #'durand-display-line-numbers) ;;;###autoload (defun durand-display-line-numbers (&optional arg) "Display absolute line numbers or not. With a positive ARG, display in relative style. With a negative ARG, display in visual style. With zero ARG, disable line numbers. See `display-line-numbers' for details." (interactive "P") (setq display-line-numbers (cond ((null arg) (not display-line-numbers)) ((> (prefix-numeric-value arg) 0) 'relative) ((< (prefix-numeric-value arg) 0) 'visual)))) ;;; Windows ;;;###autoload (defun durand-enlarge-window (&optional delta horizontal) "Make the seleted window DELTA lines taller. This is a thin wrapper around the default `enlarge-window'. If DELTA is nil, it defaults to 1. If HORIZONTAL is non-nil, this will make the seleted window DELTA lines wider instead. If DELTA is negative, shrink instead of enlarge. Also, if DELTA is 0, then maximize the selected window." (interactive "P") (cond ((eq delta 0) (enlarge-window (cond (horizontal (frame-width)) ((frame-height))) horizontal)) ((enlarge-window (prefix-numeric-value delta) horizontal)))) ;;;###autoload (defun durand-shrink-window (&optional delta horizontal) "Make the seleted window DELTA lines smaller. This is a thin wrapper around the default `shrink-window'. If DELTA is nil, it defaults to 1. If HORIZONTAL is non-nil, this will make the seleted window DELTA lines smaller instead. If DELTA is negative, enlarge instead of shrink. Also, if DELTA is 0, then minimize the selected window." (interactive "p") (cond ((eq delta 0) (shrink-window (cond (horizontal (frame-width)) ((frame-height))) horizontal)) ((shrink-window (prefix-numeric-value delta) horizontal)))) ;;;###autoload (defun durand-enlarge-window-horizontally (&optional delta) "Make selected window DELTA columns wider. This is a thin wrapper around the default `enlarge-window-horizontally'. DELTA defaults to 1. If DELTA is 0, then maximize the selected window horizontally." (interactive "p") (cond ((eq delta 0) (enlarge-window-horizontally (frame-width))) ((enlarge-window-horizontally (prefix-numeric-value delta))))) ;;;###autoload (defun durand-shrink-window-horizontally (&optional delta) "Make selected window DELTA columns wider. This is a thin wrapper around the default `shrink-window-horizontally'. DELTA defaults to 1. If DELTA is 0, then maximize the selected window horizontally." (interactive "p") (cond ((eq delta 0) (shrink-window-horizontally (frame-width))) ((shrink-window-horizontally (prefix-numeric-value delta))))) (define-key global-map (vector ?\s-o) #'other-window) (define-key global-map (vector ?\s-&) #'delete-other-windows) (define-key global-map (vector ?\s-é) #'split-window-below) ;; The following binds the key ?\s-" represented as a number, since ;; otherwise it would be interpreted as a string quote. Here #x22 = 34 ;; is the character code of the quote, and bitwise or with (ash 1 23) ;; is the effect of super. (define-key global-map (vector (logior (ash 1 23) #x22)) #'split-window-right) (define-key global-map (vector ?\s-à) #'delete-window) (define-key global-map (vector ?\C-+) #'durand-enlarge-window) (define-key global-map (vector ?\C-=) #'durand-shrink-window) (define-key global-map (vector ?\C-x ?\{) #'durand-shrink-window-horizontally) (define-key global-map (vector ?\C-x ?\}) #'durand-enlarge-window-horizontally) (define-key global-map (vector ?\s-f) #'find-file) (define-key global-map (vector ?\s-F) #'find-file-other-window) (define-key global-map (vector ?\s-d) #'dired) (define-key global-map (vector ?\s-D) #'dired-other-window) ;; (define-key global-map (vector ?\s-v) #'durand-focus-completion-or-minibuffer) (define-key global-map (vector ?\s-v) #'view-mode) ;; I like to use winner mode now (require 'winner) (winner-mode 1) ;;; Escape triggers super (define-key input-decode-map (vector 27) nil) (define-key input-decode-map (vector 27) #'event-apply-hyper-modifier) (define-key key-translation-map (vector 27) #'event-apply-hyper-modifier) ;;; Don't ask me to confirm! ;;;###autoload (defun durand-bookmark-completing-read (prompt &optional default) "Prompting with PROMPT, read a bookmark name in completion. PROMPT will get a \": \" stuck on the end no matter what, so you probably don't want to include one yourself. Optional arg DEFAULT is a string to return if the user input is empty. If DEFAULT is nil then return empty string for empty input. Don't ask me to confirm my choice. --- Durand" (bookmark-maybe-load-default-file) ; paranoia (if (listp last-nonmenu-event) (bookmark-menu-popup-paned-menu t prompt (if bookmark-sort-flag (sort (bookmark-all-names) 'string-lessp) (bookmark-all-names))) (let* ((completion-ignore-case bookmark-completion-ignore-case) (default (unless (equal "" default) default)) (prompt (concat prompt (if default (format " (%s): " default) ": ")))) (completing-read prompt (lambda (string pred action) (if (eq action 'metadata) '(metadata (category . bookmark)) (complete-with-action action bookmark-alist string pred))) nil t nil 'bookmark-history default)))) (advice-add 'bookmark-completing-read :override #'durand-bookmark-completing-read) (define-key global-map (vector ?\s-Z) #'undo-only) ;;; Don't create a scratch buffer (defun durand-read-buffer-to-switch (prompt) "Read the name of a buffer to switch to, prompting with PROMPT. Return the name of the buffer as a string. This function is intended for the `switch-to-buffer' family of commands since these need to omit the name of the current buffer from the list of completions and default values. Modified to stop creating a buffer, when there is only one buffer left. --- 2022-11-17 22:56:20.746698" (let ((rbts-completion-table (internal-complete-buffer-except)) (default-buffer (other-buffer (current-buffer)))) (cond ((string= (buffer-name default-buffer) "*scratch*") (kill-buffer default-buffer) (setq default-buffer (current-buffer)))) (minibuffer-with-setup-hook (lambda () (setq minibuffer-completion-table rbts-completion-table) ;; Since rbts-completion-table is built dynamically, we ;; can't just add it to the default value of ;; icomplete-with-completion-tables, so we add it ;; here manually. (if (and (boundp 'icomplete-with-completion-tables) (listp icomplete-with-completion-tables)) (setq-local icomplete-with-completion-tables (cons rbts-completion-table icomplete-with-completion-tables)))) (read-buffer prompt default-buffer (confirm-nonexistent-file-or-buffer))))) (advice-add #'read-buffer-to-switch :override #'durand-read-buffer-to-switch) ;;; Clear invisible buffers (defvar durand-invisible-buffer-unsafe-regex (rx-to-string '(or "Minibuf" "Echo Area" "code-conversion-work" "code-converting-work" "address" (seq "pdf" (optional ?-) "info")) t) "I do not want to kill some invisible buffers. This variable is a regular expression that should match those buffers that I do not want to kill automatically.") (defun durand-clear-invisible-buffers () "Kill invisible buffers that are not known to be safe to kill." (interactive) (let ((count 0)) (mapc (lambda (buffer) (let ((name (buffer-name buffer)) (inhibit-changing-match-data t)) (cond ;; ignore non-invisible buffers ((or (= (length name) 0) (not (= (aref name 0) 32)))) ;; ignore some "potentially unsafe" buffers ((string-match durand-invisible-buffer-unsafe-regex name)) ;; ignore buffers that are associated with live processes ((get-buffer-process buffer)) ((setq count (1+ count)) (kill-buffer buffer))))) (buffer-list)) (message "Cleared %d invisible buffers" count))) (define-key global-map (vector 3 ?k) #'durand-clear-invisible-buffers) ;;; Save close ;;;###autoload (defun durand-confirm-execute (fun &rest args) "Ask for confirmation to execute FUN with ARGS." (cond ((y-or-n-p (format "Sure to execute %S" this-command)) (apply fun args)) ((message "No need to thank me. :-)")))) (advice-add #'save-buffers-kill-terminal :around #'durand-confirm-execute) ;;; Use ripgrep for xref (setq xref-search-program 'ripgrep) ;;; Package management ;;;###autoload (defvar package-dir "/Users/durand/elisp_packages/" "The directory containing packages.") ;;;###autoload (defmacro use-package (package-path package-name &rest configs) "Add PACKAGE-PATH to `load-path' and require PACKAGE-NAME. The remaining CONFIGS are evaluated after the package is loaded." (declare (indent 2) (debug defun)) `(progn (add-to-list 'load-path (expand-file-name ,package-path ,package-dir)) (require ,package-name) ,@configs)) ;;; Go to parent in the file name completion map (defun durand-delete-goto-parent-dir (n &optional kill-flag) "Go to the parent directory when appropriate in completing file \ names. This goes to the parent directory when the character before the point is a non-escaped forward slash. N and KILL-FLAG have the same meaning as `delete-backward-char'." (interactive "p\nP") (cond ((and (not (use-region-p)) ;; This variable has been said to be supposed to be obsolete ;; since 2011, but it remains today, so I just use it here. minibuffer-completing-file-name (minibufferp nil t) (member (char-before) (list ?/ ?~)) (/= (char-before (max (1- (point)) (minibuffer-prompt-end))) ?\\)) ;; HACK (let* ((start (let ((result (point)) (begin (minibuffer-prompt-end)) foundp) (while (null foundp) (setq foundp t) (cond ((or (and (= (char-before result) ?~) (member (char-before (max (1- result) begin)) (list ?/ ?~))) (and (= (char-before result) ?/) (= (char-before (max (1- result) begin)) ?/))) (setq result (max (1- result) begin))) ((= result begin)) ((setq result (1- result)) (setq foundp nil)))) result)) (file-name (expand-file-name (buffer-substring start (point)))) (parent (cond ((string= file-name "/") "") ((file-name-directory (directory-file-name file-name)))))) (delete-region start (point)) (cond ((stringp parent) (insert (abbreviate-file-name parent)))))) ((delete-backward-char n kill-flag)))) (define-key minibuffer-local-filename-completion-map (vector ?\d) #'durand-delete-goto-parent-dir)