From da92f0693f420dd1edcc3bd90629a235a7cc6100 Mon Sep 17 00:00:00 2001 From: Ashley Duckworth Date: Wed, 20 Jan 2021 12:57:24 -0600 Subject: [PATCH] UI, Persistence --- config.org | 386 ++++++++++++++++++++++++++++++++++++++++++++------ early-init.el | 8 +- init.el | 4 +- 3 files changed, 346 insertions(+), 52 deletions(-) diff --git a/config.org b/config.org index 2a9c97b..ac18133 100644 --- a/config.org +++ b/config.org @@ -1,6 +1,6 @@ #+TITLE: Emacs configuration, literate-style #+AUTHOR: Case Duckworth -#+PROPERTY: header-args :tangle yes :tangle-mode (identity #o444) :comments both :mkdirp yes +#+PROPERTY: header-args :tangle yes :tangle-mode (identity #o444) :comments both * Settings @@ -56,11 +56,10 @@ These should not require non-built-in packages. (add-function :before after-focus-change-function #'acdw/set-custom-faces) (defmacro cussface (face spec &optional _docstring) - "Add the form (FACE SPEC) to `acdw--custom-faces', and add a - hook to run `acdw/set-custom-faces' after init." + "Add the form (FACE SPEC) to `acdw--custom-faces'." (declare (doc-string 3) - (indent 2)) - `(add-to-list acdw--custom-faces '(,face ,spec))) + (indent defun)) + `(add-to-list 'acdw--custom-faces '(,face ,spec))) #+end_src *** Only do something when Emacs is unfocused @@ -110,43 +109,6 @@ My name and email address. (blink-cursor-mode -1) #+end_src -*** Dialogs and alerts - -**** Don't use a dialog box - -#+begin_src emacs-lisp - (cuss use-dialog-box nil) -#+end_src - -**** Yes or no questions - -#+begin_src emacs-lisp - (fset 'yes-or-no-p #'y-or-n-p) -#+end_src - -**** The Bell - -#+begin_src emacs-lisp - ;; Don't flash the whole screen on bell - (cuss visible-bell nil) - - ;; Instead, flash the mode line - (cuss ring-bell-function #'flash-mode-line) - - (defun flash-mode-line () - (invert-face 'mode-line) - (run-with-timer 0.2 nil #'invert-face 'mode-line)) -#+end_src - -t*** Minibuffer - -**** Keep the cursor away from the minibuffer prompt - -#+begin_src emacs-lisp - (cuss minibuffer-prompt-properties - '(read-only t cursor-intangible t face minibuffer-prompt)) -#+end_src - *** Tabs **** Tab names should be current buffer + a count of windows @@ -237,20 +199,352 @@ and have made a custom fringe bitmap. #+begin_src emacs-lisp (cuss visual-line-fringe-indicators '(left-curly-arrow nil)) +#+end_src - ;; And make the `left-curly-arrow' indicator less distracting. +***** Customize fringe bitmaps +****** Curly arrows (continuation lines) + +#+begin_src emacs-lisp (define-fringe-bitmap 'left-curly-arrow [#b11000000 #b01100000 #b00110000 #b00011000]) + + (define-fringe-bitmap 'right-curly-arrow + [#b00011000 + #b00110000 + #b01100000 + #b11000000]) +#+end_src + +****** Arrows (truncation lines) + +#+begin_src emacs-lisp + (define-fringe-bitmap 'left-arrow + [#b00000000 + #b01010100 + #b01010100 + #b00000000]) + + (define-fringe-bitmap 'right-arrow + [#b00000000 + #b00101010 + #b00101010 + #b00000000]) #+end_src *** Windows +**** Winner mode + +I don't really /use/ winner-mode as of yet, but it seems like a really +good thing to have. It lets you move between window configurations +with =C-c <-/->=. + +#+begin_src emacs-lisp + (when (fboundp 'winner-mode) + (winner-mode +1)) +#+end_src + +**** Switch windows, or buffers if there's only one + +from [[https://www.reddit.com/r/emacs/comments/kz347f/what_parts_of_your_config_do_you_like_best/gjlnp2c/][u/astoff1]]. + +#+begin_src emacs-lisp + (defun other-window-or-buffer () + "Switch to the other window, or previous buffer." + (interactive) + (if (eq (count-windows) 1) + (switch-to-buffer nil) + (other-window 1))) +#+end_src + *** Buffers +**** Startup buffers + +I don't want to see Emacs's splash screen, and I want the =*scratch*= +buffer to have a little message. + +#+begin_src emacs-lisp + (cuss inhibit-startup-screen t + "Don't show the startup buffer.") + + (cuss initial-buffer-choice t + "Start with *scratch*.") + + (cuss initial-scratch-message + (concat ";; Hello, " (nth 0 (split-string user-full-name)) "!\n" + ";; Happy hacking ...")) +#+end_src + +**** Immortal =*scratch*= buffer + +I don't want to accidentally kill the =*scratch*= buffer. + +#+begin_src emacs-lisp + (defun immortal-scratch () + (if (eq (current-buffer) (get-buffer "*scratch*")) + (progn (bury-buffer) + nil) + t)) + + (add-hook 'kill-buffer-query-functions #'immortal-scratch) +#+end_src + +**** Uniquify buffers + +I like the =forward= style, which uniquifies buffers by including path +elements up the tree until the names are unique. + +#+begin_src emacs-lisp + (require 'uniquify) + (cuss uniquify-buffer-name-style 'forward) +#+end_src + +**** Kill buffers more smarter-ly + +#+begin_src emacs-lisp + (defun kill-a-buffer (&optional prefix) + "Kill buffers and windows sanely. + + `kill-a-buffer' works based on the prefix argument as follows: + + - 0 => kill the CURRENT buffer and window + - 4 (C-u) => kill the OTHER window and its buffer + - 16 (C-u C-u) => kill ALL OTHER buffers and windows + + Prompt iff there are unsaved changes." + (interactive "P") + (pcase (or (car prefix) 0) + (0 (kill-current-buffer) + (unless (one-window-p) (delete-window))) + (4 (other-window 1) + (kill-current-buffer) + (unless (one-window-p) (delete-window))) + (16 (mapc #'kill-buffer (delq (current-buffer) (buffer-list))) + (delete-other-windows)))) +#+end_src + +*** Fonts + +**** Function: =set-face-from-alternatives= + +To be honest, this might be better off using =cussface=, but that's +another story for another day. + +#+begin_src emacs-lisp + (defun set-face-from-alternatives (face frame &rest fontspecs) + "Set FACE on FRAME from first available font from FONTSPECS. + FACE and FRAME work the same as with `set-face-attribute'." + (catch :return + (dolist (spec fontspecs) + (when-let ((found (find-font (apply #'font-spec spec)))) + (set-face-attribute face frame :font found) + (throw :return found))))) +#+end_src + +**** Add a hook to setup fonts after the first window focus change + +Of course, I only need to setup the fonts on a graphical session. + +#+begin_src emacs-lisp + (when (display-graphic-p) + (add-function :before after-focus-change-function #'acdw/setup-fonts)) +#+end_src + +**** Setup my fonts + +Notice that this function removes itself from +=after-focus-change-function=, since ideally you'll only need to set +the fonts once. + +#+begin_src emacs-lisp + (defun acdw/setup-fonts () + "Setup fonts. + + This has to happen after the frame is setup for the first time, + so it should be added to `after-focus-change-function'. It + removes itself." + (set-face-from-alternatives 'default nil + '(:family "Input Mono" + :slant normal + :weight normal + :height 110) + '(:family "Consolas" + :slant normal + :weight normal + :height 100)) + ;; `fixed-pitch' should just inherit from `default' + (set-face-attribute 'fixed-pitch nil :inherit 'default) + + (set-face-from-alternatives 'variable-pitch nil + '(:family "Input Sans" + :slant normal + :weight normal) + '(:family "Georgia" + :slant normal + :weight normal)) + + (remove-function after-focus-change-function #'acdw/setup-fonts)) +#+end_src + +**** Underlines + +#+begin_src emacs-lisp + (cuss x-underline-at-descent-line t) +#+end_src + +** Interactivity +*** Dialogs and alerts + +**** Don't use a dialog box + +#+begin_src emacs-lisp + (cuss use-dialog-box nil) +#+end_src + +**** Yes or no questions + +#+begin_src emacs-lisp + (fset 'yes-or-no-p #'y-or-n-p) +#+end_src + +**** The Bell + +#+begin_src emacs-lisp + ;; Don't flash the whole screen on bell + (cuss visible-bell nil) + + ;; Instead, flash the mode line + (cuss ring-bell-function #'flash-mode-line) + + (defun flash-mode-line () + (invert-face 'mode-line) + (run-with-timer 0.2 nil #'invert-face 'mode-line)) +#+end_src + +t*** Minibuffer + +*** Minibuffer + +**** Keep the cursor away from the minibuffer prompt + +#+begin_src emacs-lisp + (cuss minibuffer-prompt-properties + '(read-only t cursor-intangible t face minibuffer-prompt)) +#+end_src + +**** Enable recursive minibuffer + +#+begin_src emacs-lisp + (cuss enable-recursive-minibuffers t) +#+end_src + +**** Show how deep the minibuffer goes in the modeline + +#+begin_src emacs-lisp + (minibuffer-depth-indicate-mode +1) +#+end_src + +*** Completing-read + +**** Shadow file names + +When typing =~= or =/= in the file-selection dialog, Emacs "pretends" +that you've typed them at the beginning of the line. By default, +however, it only /fades out/ the previous contents of the line. I +want to /hide/ those contents. + +#+begin_src emacs-lisp + (cuss file-name-shadow-properties '(invisible t)) + + (file-name-shadow-mode +1) +#+end_src + +**** Ignore case + +#+begin_src emacs-lisp + (cuss completion-ignore-case t) + (cuss read-buffer-completion-ignore-case t) + (cuss read-file-name-completion-ignore-case t) +#+end_src + +** Persistence + +*** Minibuffer history + +The =savehist= package saves the minibuffer history between sessions. +It can also save some other variables alongside the minibuffer +history. Since storage is cheap, I'm also going to keep all my +history. + +#+begin_src emacs-lisp + (require 'savehist) + + (cuss savehist-additional-variables + '(kill-ring + search-ring + regexp-search-ring)) + + ;; Don't truncate history + (cuss history-length t) + + ;; Delete history duplicates + (cuss history-delete-duplicates t) + + (savehist-mode +1) +#+end_src + +*** File places + +The =saveplace= package saves where I've been in the files I've +visited, so I can return back to them. + +#+begin_src emacs-lisp + (require 'saveplace) + + ;; Forget the place in unreadable files + (cuss save-place-forget-unreadable-files t) + + (save-place-mode +1) +#+end_src + +*** Recent files + +#+begin_src emacs-lisp + (require 'recentf) + + ;; Limit the number of items in the recentf menu + (cuss recentf-max-menu-items 100) + ;; But not the number of items in the actual list + (cuss recentf-max-saved-items nil) + + (recentf-mode +1) +#+end_src + +**** Save the recentf list periodically + +#+begin_src emacs-lisp + (defun acdw/maybe-save-recentf () + "Save `recentf-file every five minutes, but only when out of focus." + (defvar recentf--last-save (time-convert nil 'integer) + "When we last ran `recentf-save-list'.") + + (when (> (time-convert (time-since recentf--last-save) 'integer) + (* 60 5)) + (setq recentf--last-save (time-convert nil 'integer)) + (acdw/when-unfocused #'recentf-save-list))) + + (add-function :after after-focus-change-function #'acdw/maybe-save-recentf) +#+end_src + +** Files + +*** Encoding + * System-specific I use both Linux (at home) and Windows (at work). To make Emacs @@ -286,7 +580,7 @@ settings and written some ancillary scripts. **** em :PROPERTIES: -:header-args: :tangle-mode (identity #o755) :tangle bin/em +:header-args: :tangle-mode (identity #o755) :tangle bin/em :mkdirp yes :END: Here's a wrapper script that'll start =emacs --daemon= if there isn't @@ -302,7 +596,7 @@ your =$PATH= somewhere. **** emacsclient.desktop :PROPERTIES: -:header-args: :tangle bin/emacsclient.desktop +:header-args: :tangle bin/emacsclient.desktop :mkdirp yes :END: I haven't really tested this yet, but it should allow me to open other @@ -338,7 +632,7 @@ section come from [[https://github.com/termitereform/JunkPile/blob/master/emacs- *** Scripts :PROPERTIES: -:header-args: :noweb tangle +:header-args: :noweb tangle :mkdirp yes :END: **** Common variables diff --git a/early-init.el b/early-init.el index f879ee4..2e78e43 100644 --- a/early-init.el +++ b/early-init.el @@ -7,21 +7,21 @@ ;; I use =straight.el= instead. -;; [[file:config.org::*Disable loading of =package.el=][Disable loading of =package.el=:1]] +;; [[file:~/.emacs.d/config.org::*Disable loading of =package.el=][Disable loading of =package.el=:1]] (setq package-enable-at-startup nil) ;; Disable loading of =package.el=:1 ends here ;; Don't resize the frame when loading fonts -;; [[file:config.org::*Don't resize the frame when loading fonts][Don't resize the frame when loading fonts:1]] +;; [[file:~/.emacs.d/config.org::*Don't resize the frame when loading fonts][Don't resize the frame when loading fonts:1]] (setq frame-inhibit-implied-resize t) ;; Don't resize the frame when loading fonts:1 ends here ;; Resize frame by pixels -;; [[file:config.org::*Resize frame by pixels][Resize frame by pixels:1]] +;; [[file:~/.emacs.d/config.org::*Resize frame by pixels][Resize frame by pixels:1]] (setq frame-resize-pixelwise t) ;; Resize frame by pixels:1 ends here @@ -35,7 +35,7 @@ ;; what I have for now. -;; [[file:config.org::*Shoe-horned from elsewhere in =config.org=][Shoe-horned from elsewhere in =config.org=:1]] +;; [[file:~/.emacs.d/config.org::*Shoe-horned from elsewhere in =config.org=][Shoe-horned from elsewhere in =config.org=:1]] (add-to-list 'default-frame-alist '(tool-bar-lines . 0)) diff --git a/init.el b/init.el index b902c0b..d0e30b2 100644 --- a/init.el +++ b/init.el @@ -5,7 +5,7 @@ ;; Prefer newer files to older files -;; [[file:config.org::*Prefer newer files to older files][Prefer newer files to older files:1]] +;; [[file:~/.emacs.d/config.org::*Prefer newer files to older files][Prefer newer files to older files:1]] (setq load-prefer-newer t) ;; Prefer newer files to older files:1 ends here @@ -16,7 +16,7 @@ ;; directly from Org if it's newer. -;; [[file:config.org::*Load the config][Load the config:1]] +;; [[file:~/.emacs.d/config.org::*Load the config][Load the config:1]] (let* (;; Speed up init (gc-cons-threshold most-positive-fixnum) (file-name-handler-alist nil)