chiark / gitweb /
el/dot-emacs.el (mdw-regexp): Sort the input list.
[profile] / el / dot-emacs.el
index ded403003f853226aaf47fbec01416c9c93e0e14..7fe8757bc67f82cdb07aea60aa32f526abf235e5 100644 (file)
 ;;;--------------------------------------------------------------------------
 ;;; Check command-line.
 
+(defgroup mdw nil
+  "Customization for mdw's Emacs configuration."
+  :prefix "mdw-")
+
 (defun mdw-check-command-line-switch (switch)
   (let ((probe nil) (next command-line-args) (found nil))
     (while next
@@ -51,15 +55,14 @@ (setq mdw-splashy-startup
 ;;; Some general utilities.
 
 (eval-when-compile
-  (unless (fboundp 'make-regexp)
-    (load "make-regexp"))
+  (unless (fboundp 'make-regexp) (load "make-regexp"))
   (require 'cl))
 
 (defmacro mdw-regexps (&rest list)
   "Turn a LIST of strings into a single regular expression at compile-time."
   (declare (indent nil)
           (debug 0))
-  `',(make-regexp list))
+  `',(make-regexp (sort (copy-list list) #'string<)))
 
 (defun mdw-wrong ()
   "This is not the key sequence you're looking for."
@@ -72,20 +75,28 @@ (defun mdw-emacs-version-p (major &optional minor)
       (and (= emacs-major-version major)
           (>= emacs-minor-version (or minor 0)))))
 
+(defun mdw-submode-p (mode parent)
+  "Return non-nil if MODE is indirectly derived from PARENT."
+  (let ((answer nil))
+    (while (cond ((eq mode parent) (setq answer t) nil)
+                (t (setq mode (get mode 'derived-mode-parent)))))
+    answer))
+
 ;; Some error trapping.
 ;;
 ;; If individual bits of this file go tits-up, we don't particularly want
 ;; the whole lot to stop right there and then, because it's bloody annoying.
 
-(defmacro trap (&rest forms)
-  "Execute FORMS without allowing errors to propagate outside."
-  (declare (indent 0)
-          (debug t))
-  `(condition-case err
-       ,(if (cdr forms) (cons 'progn forms) (car forms))
-     (error (message "Error (trapped): %s in %s"
-                    (error-message-string err)
-                    ',forms))))
+(eval-and-compile
+  (defmacro trap (&rest forms)
+    "Execute FORMS without allowing errors to propagate outside."
+    (declare (indent 0)
+            (debug t))
+    `(condition-case err
+        ,(if (cdr forms) (cons 'progn forms) (car forms))
+       (error (message "Error (trapped): %s in %s"
+                      (error-message-string err)
+                      ',forms)))))
 
 ;; Configuration reading.
 
@@ -113,12 +124,14 @@ (defun mdw-config (sym)
 
 ;; Width configuration.
 
-(defvar mdw-column-width
+(defcustom mdw-column-width
   (string-to-number (or (mdw-config 'emacs-width) "77"))
-  "Width of Emacs columns.")
-(defvar mdw-text-width mdw-column-width
-  "Expected width of text within columns.")
-(put 'mdw-text-width 'safe-local-variable 'integerp)
+  "Width of Emacs columns."
+  :type 'integer)
+(defcustom mdw-text-width mdw-column-width
+  "Expected width of text within columns."
+  :type 'integer
+  :safe 'integerp)
 
 ;; Local variables hacking.
 
@@ -240,6 +253,18 @@ (defun mdw-divvy-window (&optional width)
       (other-window 1))
     (select-window win)))
 
+(defun mdw-frame-width-quantized-p (frame-width column-width)
+  "Return whether the FRAME-WIDTH was chosen specifically for COLUMN-WIDTH."
+  (let ((sb-width (mdw-horizontal-window-overhead)))
+    (zerop (mod (+ frame-width sb-width)
+               (+ column-width sb-width)))))
+
+(defun mdw-frame-width-for-columns (columns width)
+  "Return the preferred width for a frame with so many COLUMNS of WIDTH."
+  (let ((sb-width (mdw-horizontal-window-overhead)))
+    (- (* columns (+ width sb-width))
+       sb-width)))
+
 (defun mdw-set-frame-width (columns &optional width)
   "Set the current frame to be the correct width for COLUMNS columns.
 
@@ -249,24 +274,103 @@ (defun mdw-set-frame-width (columns &optional width)
 P")
   (setq width (if width (prefix-numeric-value width)
                (mdw-preferred-column-width)))
-  (let ((sb-width (mdw-horizontal-window-overhead)))
-    (set-frame-width (selected-frame)
-                    (- (* columns (+ width sb-width))
-                       sb-width))
-    (mdw-divvy-window width)))
+  (set-frame-width (selected-frame)
+                  (mdw-frame-width-for-columns columns width))
+  (mdw-divvy-window width))
 
-(defvar mdw-frame-width-fudge
+(defcustom mdw-frame-width-fudge
   (cond ((<= emacs-major-version 20) 1)
        ((= emacs-major-version 26) 3)
        (t 0))
   "The number of extra columns to add to the desired frame width.
 
-This is sadly necessary because Emacs 26 is broken in this regard.")
+This is sadly necessary because Emacs 26 is broken in this regard."
+  :type 'integer)
+
+(defcustom mdw-frame-colour-alist
+  '((black . ("#000000" . "#ffffff"))
+    (red . ("#2a0000" . "#ffffff"))
+    (green . ("#002a00" . "#ffffff"))
+    (blue . ("#00002a" . "#ffffff")))
+  "Alist mapping symbol names to (FOREGROUND . BACKGROUND) colour pairs."
+  :type '(alist :key-type symbol :value-type (cons color color)))
+
+(defun mdw-set-frame-colour (colour &optional frame)
+  (interactive "xColour name or (FOREGROUND . BACKGROUND) pair: 
+")
+  (when (and colour (symbolp colour))
+    (let ((entry (assq colour mdw-frame-colour-alist)))
+      (unless entry (error "Unknown colour `%s'" colour))
+      (setf colour (cdr entry))))
+  (set-frame-parameter frame 'background-color (car colour))
+  (set-frame-parameter frame 'foreground-color (cdr colour)))
+
+;; Window configuration switching.
+
+(defvar mdw-current-window-configuration nil
+  "The current window configuration register name, or `nil'.")
+
+(defun mdw-switch-window-configuration (register &optional no-save)
+  "Switch make REGISTER be the new current window configuration.
+If a current window configuration register is established, and
+NO-SAVE is nil, then save the current window configuration to
+that register first.
+
+Signal an error if the new register contains something other than
+a window configuration.  If the register is unset then save the
+current window configuration to it immediately.
+
+With one or three C-u, or an odd numeric prefix argument, set
+NO-SAVE, so the previous window configuration register is left
+unchanged.
+
+With two or three C-u, or a prefix argument which is an odd
+multiple of 2, just clear the record of the current window
+configuration register, so that the next switch doesn't save the
+prevailing configuration."
+  (interactive
+   (let ((arg current-prefix-arg))
+     (list (if (or (and (consp arg) (= (car arg) 16) (= (car arg) 64))
+                  (and (integerp arg) (not (zerop (logand arg 2)))))
+              nil
+            (register-read-with-preview "Switch to window configuration: "))
+          (or (and (consp arg) (= (car arg) 4) (= (car arg) 64))
+              (and (integerp arg) (not (zerop (logand arg 1))))))))
+
+  (let ((previous mdw-current-window-configuration)
+       (current-windows (list (current-window-configuration)
+                              (point-marker)))
+       (register-value (and register (get-register register))))
+    (when (and mdw-current-window-configuration (not no-save))
+      (set-register mdw-current-window-configuration current-windows))
+    (cond ((null register)
+          (setq mdw-current-window-configuration nil)
+          (if previous
+              (message "Left window configuration `%c'." previous)
+            (message "Nothing to do!")))
+         ((not (or (null register-value)
+                   (and (consp register-value)
+                        (window-configuration-p (car register-value))
+                        (integer-or-marker-p (cadr register-value))
+                        (null (caddr register-value)))))
+          (error "Register `%c' is not a window configuration" register))
+         (t
+          (cond ((null register-value)
+                 (set-register register current-windows)
+                 (message "Started new window configuration `%c'."
+                          register))
+                (t
+                 (set-window-configuration (car register-value))
+                 (goto-char (cadr register-value))
+                 (message "Switched to window configuration `%c'."
+                          register)))
+          (setq mdw-current-window-configuration register)))))
 
 ;; Don't raise windows unless I say so.
 
-(defvar mdw-inhibit-raise-frame nil
-  "*Whether `raise-frame' should do nothing when the frame is mapped.")
+(defcustom mdw-inhibit-raise-frame nil
+  "Whether `raise-frame' should do nothing when the frame is mapped."
+  :type 'boolean)
 
 (defadvice raise-frame
     (around mdw-inhibit (&optional frame) activate compile)
@@ -342,6 +446,9 @@ (defadvice exchange-point-and-mark
 
 ;; Functions for sexp diary entries.
 
+(defvar mdw-diary-for-org-mode-p nil
+  "Display diary along with the agenda?")
+
 (defun mdw-not-org-mode (form)
   "As FORM, but not in Org mode agenda."
   (and (not mdw-diary-for-org-mode-p)
@@ -421,13 +528,13 @@ (defun mdw-todo (&optional when)
                                (nth 2 when))))))))
     (eq w d)))
 
-(defvar mdw-diary-for-org-mode-p nil)
-
 (defadvice org-agenda-list (around mdw-preserve-links activate)
   (let ((mdw-diary-for-org-mode-p t))
     ad-do-it))
 
-(defvar diary-time-regexp nil)
+(defcustom diary-time-regexp nil
+  "Regexp matching times in the diary buffer."
+  :type 'regexp)
 
 (defadvice diary-add-to-list (before mdw-trim-leading-space compile activate)
   "Trim leading space from the diary entry string."
@@ -471,7 +578,7 @@ (defadvice org-bbdb-anniversaries (after mdw-fixup-list compile activate)
 
 ;; Fighting with Org-mode's evil key maps.
 
-(defvar mdw-evil-keymap-keys
+(defcustom mdw-evil-keymap-keys
   '(([S-up] . [?\C-c up])
     ([S-down] . [?\C-c down])
     ([S-left] . [?\C-c left])
@@ -482,7 +589,9 @@ (defvar mdw-evil-keymap-keys
     (([M-right] [?\e right]) . [C-right]))
   "Defines evil keybindings to clobber in `mdw-clobber-evil-keymap'.
 The value is an alist mapping evil keys (as a list, or singleton)
-to good keys (in the same form).")
+to good keys (in the same form)."
+  :type '(alist :key-type (choice key-sequence (repeat key-sequence))
+               :value-type key-sequence))
 
 (defun mdw-clobber-evil-keymap (keymap)
   "Replace evil key bindings in the KEYMAP.
@@ -506,7 +615,7 @@       (define-key keymap key nil))
        (dolist (key replacements)
          (define-key keymap key binding))))))
 
-(defvar mdw-org-latex-defs
+(defcustom mdw-org-latex-defs
   '(("strayman"
      "\\documentclass{strayman}
 \\usepackage[utf8]{inputenc}
@@ -516,7 +625,13 @@ (defvar mdw-org-latex-defs
      ("\\subsection{%s}" . "\\subsection*{%s}")
      ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
      ("\\paragraph{%s}" . "\\paragraph*{%s}")
-     ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))))
+     ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))
+  "Additional LaTeX class definitions."
+  :type '(alist :key-type string
+               :value-type (list string
+                                 (alist :inline t
+                                        :key-type string
+                                        :value-type string))))
 
 (eval-after-load "org-latex"
   '(setq org-export-latex-classes
@@ -555,6 +670,52 @@ (setq glasses-separator "-"
 
 ;; Some hacks to do with window placement.
 
+(defvar mdw-designated-window nil
+  "The window chosen by `mdw-designate-window', or nil.")
+
+(defun mdw-designated-window-display-buffer-function (buffer not-this-window)
+  "Display buffer function to use the designated window."
+  (unless mdw-designated-window (error "No designated window!"))
+  (prog1 mdw-designated-window
+    (with-selected-window mdw-designated-window (switch-to-buffer buffer))
+    (setq mdw-designated-window nil
+         display-buffer-function nil)))
+
+(defun mdw-display-buffer-in-designated-window (buffer alist)
+  "Display function to use the designated window."
+  (prog1 mdw-designated-window
+    (when mdw-designated-window
+      (with-selected-window mdw-designated-window
+       (switch-to-buffer buffer nil t)))
+    (setq mdw-designated-window nil)))
+
+(defun mdw-designate-window (cancel)
+  "Use the selected window for the next pop-up buffer.
+With a prefix argument, clear the designated window."
+  (interactive "P")
+  (let ((window (selected-window)))
+    (cond (cancel
+          (setq mdw-designated-window nil)
+          (unless (mdw-emacs-version-p 24)
+            (setq display-buffer-function nil))
+          (message "Window designation cleared."))
+         ((window-dedicated-p window)
+          (error "Window is dedicated to its buffer."))
+         (t
+          (setq mdw-designated-window window)
+          (unless (mdw-emacs-version-p 24)
+            (setq display-buffer-function
+                    #'mdw-designated-window-display-buffer-function))
+          (message "Window designated.")))))
+
+(when (mdw-emacs-version-p 24)
+  (setq display-buffer-base-action
+         (let* ((action display-buffer-base-action)
+                (funcs (car action))
+                (alist (cdr action)))
+           (cons (cons 'mdw-display-buffer-in-designated-window funcs)
+                 alist))))
+
 (defun mdw-clobber-other-windows-showing-buffer (buffer-or-name)
   "Arrange that no windows on other frames are showing BUFFER-OR-NAME."
   (interactive "bBuffer: ")
@@ -596,6 +757,70 @@ (defadvice display-buffer (before mdw-inhibit-other-frames activate)
 (setq even-window-sizes nil
       even-window-heights nil)
 
+(setq display-buffer-reuse-frames nil)
+
+(defun mdw-last-window-in-frame-p (window)
+  "Return whether WINDOW is the last in its frame."
+  (catch 'done
+    (while window
+      (let ((next (window-next-sibling window)))
+       (while (and next (window-minibuffer-p next))
+         (setq next (window-next-sibling next)))
+       (if next (throw 'done nil)))
+      (setq window (window-parent window)))
+    t))
+
+(defun mdw-display-buffer-in-tolerable-window (buffer alist)
+  "Try finding a tolerable window in which to display BUFFER.
+Begone, foul DWIMmerlaik!
+
+This is all totally subject to arbitrary change in the future, but the
+emphasis is on predictability rather than crazy DWIMmery."
+  (let* ((selected (selected-window)) chosen
+        (full-height-p (window-full-height-p selected))
+        (full-width-p (window-full-width-p selected)))
+    (cond
+
+     ((and full-height-p full-width-p)
+      ;; We're basically the only window in the frame.  If we want to get
+      ;; anywhere, we'll have to split the window.
+
+      (let ((width (window-width selected))
+           (preferred-width (mdw-preferred-column-width)))
+       (if (and (>= width (mdw-frame-width-for-columns 2 preferred-width))
+                (mdw-frame-width-quantized-p width preferred-width))
+           (setq chosen (split-window-right preferred-width))
+         (setq chosen (split-window-below)))
+       (display-buffer-record-window 'window chosen buffer)))
+
+     ((mdw-last-window-in-frame-p selected)
+      ;; This is the last window in the frame.  I don't think I want to
+      ;; clobber the first window, so rebound and clobber the previous one
+      ;; instead.  (This obviously has the same effect if there are only two
+      ;; windows, but seems more useful if there are three.)
+
+      (setq chosen (previous-window selected 'never nil))
+      (display-buffer-record-window 'reuse chosen buffer))
+
+     (t
+      ;; There's another window in front of us.  Let's use that one.
+      (setq chosen (next-window selected 'never nil)))
+      (display-buffer-record-window 'reuse chosen buffer))
+
+    (if (eq chosen selected)
+       (error "Failed to select a different window!"))
+
+    (when chosen
+      (with-selected-window chosen (switch-to-buffer buffer)))
+    chosen))
+
+;; Hack the display actions so that they do something sensible.
+(setq display-buffer-fallback-action
+       '((display-buffer--maybe-same-window
+          display-buffer-reuse-window
+          display-buffer-pop-up-window
+          mdw-display-buffer-in-tolerable-window)))
+
 ;; Rename buffers along with files.
 
 (defvar mdw-inhibit-rename-buffer nil
@@ -809,10 +1034,13 @@ (let ((path exec-path))
 
 ;; AUTHINFO GENERIC kludge.
 
-(defvar nntp-authinfo-generic nil
+(defcustom nntp-authinfo-generic nil
   "Set to the `NNTPAUTH' string to pass on to `authinfo-kludge'.
 
-Use this to arrange for per-server settings.")
+Use this to arrange for per-server settings."
+  :type '(choice (const :tag "Use `NNTPAUTH' environment variable" nil)
+                string)
+  :safe 'stringp)
 
 (defun nntp-open-authinfo-kludge (buffer)
   "Open a connection to SERVER using `authinfo-kludge'."
@@ -907,8 +1135,8 @@ (eval-after-load 'nnimap
 (defadvice gnus-other-frame (around mdw-hack-frame-width compile activate)
   "Always arrange for mail/news frames to be 80 columns wide."
   (let ((default-frame-alist (cons `(width . ,(+ 80 mdw-frame-width-fudge))
-                                  (cl-delete 'width default-frame-alist
-                                             :key #'car))))
+                                  (delete* 'width default-frame-alist
+                                           :key #'car))))
     ad-do-it))
 
 ;; Preferred programs.
@@ -1001,8 +1229,10 @@ (defun uuencode (file &optional name)
   ;; Now actually do the thing.
   (call-process "uuencode" file t nil name))
 
-(defvar np-file "~/.np"
-  "*Where the `now-playing' file is.")
+(defcustom np-file "~/.np"
+  "Where the `now-playing' file is."
+  :type 'file
+  :safe 'stringp)
 
 (defun np (&optional arg)
   "Grabs a `now-playing' string."
@@ -1054,21 +1284,6 @@ (defun mdw-auto-revert ()
   (let ((auto-revert-check-vc-info t))
     (auto-revert-buffers)))
 
-(defun comint-send-and-indent ()
-  (interactive)
-  (comint-send-input)
-  (and mdw-auto-indent
-       (indent-for-tab-command)))
-
-(defadvice comint-line-beginning-position
-    (around mdw-calculate-it-properly () activate compile)
-  "Calculate the actual line start for multi-line input."
-  (if (or comint-use-prompt-regexp
-         (eq (field-at-pos (point)) 'output))
-      ad-do-it
-    (setq ad-return-value
-           (constrain-to-field (line-beginning-position) (point)))))
-
 ;;;--------------------------------------------------------------------------
 ;;; Dired hacking.
 
@@ -1133,14 +1348,15 @@ (defun mdw-w3m-browse-url (url &optional new-session-p)
 (eval-after-load 'w3m
   '(define-key w3m-mode-map [?\e ?\r] 'w3m-view-this-url-new-session))
 
-(defvar mdw-good-url-browsers
+(defcustom mdw-good-url-browsers
   '(browse-url-mozilla
     browse-url-generic
     (w3m . mdw-w3m-browse-url)
     browse-url-w3)
   "List of good browsers for mdw-good-url-browsers.
 Each item is a browser function name, or a cons (CHECK . FUNC).
-A symbol FOO stands for (FOO . FOO).")
+A symbol FOO stands for (FOO . FOO)."
+  :type '(repeat (choice function (cons function function))))
 
 (defun mdw-good-url-browser ()
   "Return a good URL browser.
@@ -1198,8 +1414,8 @@ (eval-after-load "w3m-search"
 
 ;; Useful variables.
 
-(defvar mdw-fill-prefix nil
-  "*Used by `mdw-line-prefix' and `mdw-fill-paragraph'.
+(defcustom mdw-fill-prefix nil
+  "Used by `mdw-line-prefix' and `mdw-fill-paragraph'.
 If there's no fill prefix currently set (by the `fill-prefix'
 variable) and there's a match from one of the regexps here, it
 gets used to set the fill-prefix for the current operation.
@@ -1229,13 +1445,14 @@ (defvar mdw-fill-prefix nil
 
 (make-variable-buffer-local 'mdw-fill-prefix)
 
-(defvar mdw-hanging-indents
+(defcustom mdw-hanging-indents
   (concat "\\(\\("
            "\\([*o+]\\|-[-#]?\\|[0-9]+\\.\\|\\[[0-9]+\\]\\|([a-zA-Z])\\)"
            "[ \t]+"
          "\\)?\\)")
-  "*Standard regexp matching parts of a hanging indent.
-This is mainly useful in `auto-fill-mode'.")
+  "Standard regexp matching parts of a hanging indent.
+This is mainly useful in `auto-fill-mode'."
+  :type 'regexp)
 
 ;; Utility functions.
 
@@ -1362,13 +1579,91 @@ (defun mdw-standard-fill-prefix (rx &optional mat)
             (match . 1)
             (pad . ,(or mat 2))))))
 
+;;;--------------------------------------------------------------------------
+;;; Printing.
+
+;; Teach PostScript about a condensed variant of Courier.  I'm using 85% of
+;; the usual width, which happens to match `mdwfonts', and David Carlisle's
+;; `pslatex'.  (Once upon a time, I used 80%, but decided consistency with
+;; `pslatex' was useful.)
+(setq ps-user-defined-prologue "
+/CourierCondensed /Courier
+/CourierCondensed-Bold /Courier-Bold
+/CourierCondensed-Oblique /Courier-Oblique
+/CourierCondensed-BoldOblique /Courier-BoldOblique
+  4 { findfont [0.85 0 0 1 0 0] makefont definefont pop } repeat
+")
+
+;; Hack `ps-print''s settings.
+(eval-after-load 'ps-print
+  '(progn
+
+     ;; Notice that the comment-delimiters should be in italics too.
+     (pushnew 'font-lock-comment-delimiter-face ps-italic-faces)
+
+     ;; Select more suitable colours for the main kinds of tokens.  The
+     ;; colours set on the Emacs faces are chosen for use against a dark
+     ;; background, and work very badly on white paper.
+     (ps-extend-face '(font-lock-comment-face "darkgreen" nil italic))
+     (ps-extend-face '(font-lock-comment-delimiter-face "darkgreen" nil italic))
+     (ps-extend-face '(font-lock-string-face "RoyalBlue4" nil))
+     (ps-extend-face '(mdw-punct-face "sienna" nil))
+     (ps-extend-face '(mdw-number-face "OrangeRed3" nil))
+
+     ;; Teach `ps-print' about my condensed varsions of Courier.
+     (setq ps-font-info-database
+            (append '((CourierCondensed
+                       (fonts (normal . "CourierCondensed")
+                              (bold . "CourierCondensed-Bold")
+                              (italic . "CourierCondensed-Oblique")
+                              (bold-italic . "CourierCondensed-BoldOblique"))
+                       (size . 10.0)
+                       (line-height . 10.55)
+                       (space-width . 5.1)
+                       (avg-char-width . 5.1)))
+                    (remove* 'CourierCondensed ps-font-info-database
+                             :key #'car)))))
+
+;; Arrange to strip overlays from the buffer before we print .  This will
+;; prevent `flyspell' from interfering with the printout.  (It would be less
+;; bad if `ps-print' could merge the `flyspell' overlay face with the
+;; underlying `font-lock' face, but it can't (and that seems hard).  So
+;; instead we have this hack.
+;;
+;; The basic trick is to copy the relevant text from the buffer being printed
+;; into a temporary buffer and... just print that.  The text properties come
+;; with the text and end up in the new buffer, and the overlays get lost
+;; along the way.  Only problem is that the headers identifying the file
+;; being printed get confused, so remember the original buffer and reinstate
+;; it when constructing the headers.
+(defvar mdw-printing-buffer)
+
+(defadvice ps-generate-header
+    (around mdw-use-correct-buffer () activate compile)
+  "Print the correct name of the buffer being printed."
+  (with-current-buffer mdw-printing-buffer
+    ad-do-it))
+
+(defadvice ps-generate
+    (around mdw-strip-overlays (buffer from to genfunc) activate compile)
+  "Strip overlays -- in particular, from `flyspell' -- before printout."
+  (with-temp-buffer
+    (let ((mdw-printing-buffer buffer))
+      (insert-buffer-substring buffer from to)
+      (ad-set-arg 0 (current-buffer))
+      (ad-set-arg 1 (point-min))
+      (ad-set-arg 2 (point-max))
+      ad-do-it)))
+
 ;;;--------------------------------------------------------------------------
 ;;; Other common declarations.
 
 ;; Common mode settings.
 
-(defvar mdw-auto-indent t
-  "Whether to indent automatically after a newline.")
+(defcustom mdw-auto-indent t
+  "Whether to indent automatically after a newline."
+  :type 'boolean
+  :safe 'booleanp)
 
 (defun mdw-whitespace-mode (&optional arg)
   "Turn on/off whitespace mode, but don't highlight trailing space."
@@ -1447,9 +1742,10 @@        (define-key map [C-S-mouse-3] 'gtags-pop-stack))))
 
 ;; Backup file handling.
 
-(defvar mdw-backup-disable-regexps nil
-  "*List of regular expressions: if a file name matches any of
-these then the file is not backed up.")
+(defcustom mdw-backup-disable-regexps nil
+  "List of regular expressions: if a file name matches any of
+these then the file is not backed up."
+  :type '(repeat regexp))
 
 (defun mdw-backup-enable-predicate (name)
   "[mdw]'s default backup predicate.
@@ -1484,15 +1780,17 @@ (add-hook 'delete-frame-functions 'mdw-last-one-out-turn-off-the-lights)
 ;;;--------------------------------------------------------------------------
 ;;; Fullscreen-ness.
 
-(defvar mdw-full-screen-parameters
+(defcustom mdw-full-screen-parameters
   '((menu-bar-lines . 0)
-    ;(vertical-scroll-bars . nil)
+    ;;(vertical-scroll-bars . nil)
     )
-  "Frame parameters to set when making a frame fullscreen.")
+  "Frame parameters to set when making a frame fullscreen."
+  :type '(alist :key-type symbol))
 
-(defvar mdw-full-screen-save
+(defcustom mdw-full-screen-save
   '(width height)
-  "Extra frame parameters to save when setting fullscreen.")
+  "Extra frame parameters to save when setting fullscreen."
+  :type '(repeat symbol))
 
 (defun mdw-toggle-full-screen (&optional frame)
   "Show the FRAME fullscreen."
@@ -1585,6 +1883,32 @@ (mdw-define-face highlight
   (((class color)) :background "cyan")
   (t :inverse-video t))
 
+(mdw-define-face viper-minibuffer-emacs (t nil))
+(mdw-define-face viper-minibuffer-insert (t nil))
+(mdw-define-face viper-minibuffer-vi (t nil))
+(mdw-define-face viper-replace-overlay
+  (((min-colors 64)) :background "darkred")
+  (((class color)) :background "red")
+  (t :inverse-video t))
+(mdw-define-face viper-search (t :inherit isearch))
+
+(mdw-define-face compilation-error
+  (((class color)) :foreground "red" :weight bold)
+  (t :weight bold))
+(mdw-define-face compilation-warning
+  (((class color)) :foreground "orange" :weight bold)
+  (t :weight bold))
+(mdw-define-face compilation-info
+  (((class color)) :foreground "green" :weight bold)
+  (t :weight bold))
+(mdw-define-face compilation-line-number
+  (t :weight bold))
+(mdw-define-face compilation-column-number
+  (((min-colors 64)) :foreground "lightgrey"))
+(setq compilation-message-face 'mdw-virgin-face)
+(setq compilation-enter-directory-face 'font-lock-comment-face)
+(setq compilation-leave-directory-face 'font-lock-comment-face)
+
 (mdw-define-face holiday-face
   (t :background "red"))
 (mdw-define-face calendar-today-face
@@ -1635,18 +1959,18 @@ (mdw-define-face font-lock-reference-face
   (t :weight bold))
 (mdw-define-face font-lock-variable-name-face
   (t :slant italic))
-(mdw-define-face font-lock-comment-delimiter-face
-  (((min-colors 64)) :slant italic :foreground "SeaGreen1")
-  (((class color)) :foreground "green")
-  (t :weight bold))
 (mdw-define-face font-lock-comment-face
   (((min-colors 64)) :slant italic :foreground "SeaGreen1")
   (((class color)) :foreground "green")
   (t :weight bold))
+(mdw-define-face font-lock-comment-delimiter-face
+  (t :inherit font-lock-comment-face))
 (mdw-define-face font-lock-string-face
   (((min-colors 64)) :foreground "SkyBlue1")
   (((class color)) :foreground "cyan")
   (t :weight bold))
+(mdw-define-face font-lock-doc-face
+  (t :inherit font-lock-string-face))
 
 (mdw-define-face message-separator
   (t :background "red" :foreground "white" :weight bold))
@@ -1928,7 +2252,9 @@ (mdw-define-face mdw-point-overlay-face
   (((class color)) :background "blue")
   (((type tty) (class mono)) :inverse-video t))
 
-(defvar mdw-point-overlay-fringe-display '(vertical-bar . vertical-bar))
+(defcustom mdw-point-overlay-fringe-display '(vertical-bar . vertical-bar)
+  "Bitmaps to display in the left and right fringes in the current line."
+  :type '(cons symbol symbol))
 
 (defun mdw-configure-point-overlay ()
   (let ((ov (make-overlay 0 0)))
@@ -2012,6 +2338,18 @@ (defun mdw-update-terminal-title ()
 
 (add-hook 'post-command-hook 'mdw-update-terminal-title)
 
+;;;--------------------------------------------------------------------------
+;;; Ediff hacking.
+
+(defvar mdw-ediff-previous-windows)
+(defun mdw-ediff-setup ()
+  (setq mdw-ediff-previous-windows (current-window-configuration)))
+(defun mdw-ediff-suspend-or-quit ()
+  (set-window-configuration mdw-ediff-previous-windows))
+(add-hook 'ediff-before-setup-hook 'mdw-ediff-setup)
+(add-hook 'ediff-quit-hook 'mdw-ediff-suspend-or-quit t)
+(add-hook 'ediff-suspend-hook 'mdw-ediff-suspend-or-quit t)
+
 ;;;--------------------------------------------------------------------------
 ;;; C programming configuration.
 
@@ -2056,7 +2394,10 @@ (defun mdw-merge-style-alists (first second)
   (let ((output nil))
     (dolist (item first)
       (let ((key (car item)) (value (cdr item)))
-       (if (string-suffix-p "-alist" (symbol-name key))
+       (if (let* ((key-name (symbol-name key))
+                  (key-len (length key-name)))
+             (and (>= key-len 6)
+                  (string= (subseq key-name (- key-len 6)) "-alist")))
            (push (cons key
                        (mdw-merge-style-alists value
                                                (cdr (assoc key second))))
@@ -2067,7 +2408,7 @@ (defun mdw-merge-style-alists (first second)
        (push item output)))
     (nreverse output)))
 
-(cl-defmacro mdw-define-c-style (name (&optional parent) &rest assocs)
+(defmacro* mdw-define-c-style (name (&optional parent) &rest assocs)
   "Define a C style, called NAME (a symbol) based on PARENT, setting ASSOCs.
 A function, named `mdw-define-c-style/NAME', is defined to actually install
 the style using `c-add-style', and added to the hook
@@ -2113,16 +2454,16 @@                    (defun-open . (add 0 c-indent-one-line-block))
                   (statement-cont . +)
                   (statement-case-intro . +)))
 
-(mdw-define-c-style mdw-trustonic-basic-c (mdw-c)
+(mdw-define-c-style mdw-trustonic-c (mdw-c)
   (c-basic-offset . 4)
+  (c-offsets-alist (access-label . -2)))
+
+(mdw-define-c-style mdw-trustonic-alec-c (mdw-trustonic-c)
   (comment-column . 0)
   (c-indent-comment-alist (anchored-comment . (column . 0))
                          (end-block . (space . 1))
                          (cpp-end-block . (space . 1))
                          (other . (space . 1)))
-  (c-offsets-alist (access-label . -2)))
-
-(mdw-define-c-style mdw-trustonic-c (mdw-trustonic-basic-c)
   (c-offsets-alist (arglist-cont-nonempty . mdw-c-indent-arglist-nested)))
 
 (defun mdw-set-default-c-style (modes style)
@@ -2938,7 +3279,8 @@ (defun mdw-fontify-rust ()
 
             ;; And anything else is punctuation.
             (list "\\(\\s.\\|\\s(\\|\\s)\\|\\s\\\\|\\s/\\)"
-                  '(0 mdw-punct-face)))))
+                  '(0 mdw-punct-face)))
+           font-lock-syntactic-face-function nil))
 
   ;; Hack key bindings.
   (local-set-key [?{] 'mdw-self-insert-and-indent)
@@ -3017,6 +3359,7 @@ (setq-default perl-indent-level 2)
 
 (setq-default cperl-indent-level 2
              cperl-continued-statement-offset 2
+             cperl-indent-region-fix-constructs nil
              cperl-continued-brace-offset 0
              cperl-brace-offset -2
              cperl-brace-imaginary-offset 0
@@ -3102,6 +3445,7 @@ (defun mdw-fontify-pythonic (keywords)
   ;; Miscellaneous fiddling.
   (mdw-standard-fill-prefix "\\([ \t]*#+[ \t]*\\)")
   (setq indent-tabs-mode nil)
+  (set (make-local-variable 'forward-sexp-function) nil)
 
   ;; Now define fontification things.
   (make-local-variable 'font-lock-keywords)
@@ -3126,11 +3470,22 @@ (defun mdw-fontify-pythonic (keywords)
 
 (defun mdw-fontify-python ()
   (mdw-fontify-pythonic
-   (mdw-regexps "and" "as" "assert" "break" "class" "continue" "def"
-               "del" "elif" "else" "except" "exec" "finally" "for"
-               "from" "global" "if" "import" "in" "is" "lambda"
-               "not" "or" "pass" "print" "raise" "return" "try"
-               "while" "with" "yield")))
+   (mdw-regexps "and" "as" "assert"
+               "break"
+               "class" "continue"
+               "def" "del"
+               "elif" "else" "except" "exec"
+               "finally" "for" "from"
+               "global"
+               "if" "import" "in" "is"
+               "lambda"
+               "not"
+               "or"
+               "pass" "print"
+               "raise" "return"
+               "try"
+               "while" "with"
+               "yield")))
 
 (defun mdw-fontify-pyrex ()
   (mdw-fontify-pythonic
@@ -4107,7 +4462,7 @@ (progn
 ;;;--------------------------------------------------------------------------
 ;;; HTML, CSS, and other web foolishness.
 
-(setq-default css-indent-offset 2)
+(setq-default css-indent-offset 8)
 
 ;;;--------------------------------------------------------------------------
 ;;; SGML hacking.
@@ -4135,21 +4490,21 @@ (defun mdw-sgml-mode ()
 ;;;--------------------------------------------------------------------------
 ;;; Configuration files.
 
-(defvar mdw-conf-quote-normal nil
-  "*Control syntax category of quote characters `\"' and `''.
+(defcustom mdw-conf-quote-normal nil
+  "Control syntax category of quote characters `\"' and `''.
 If this is `t', consider quote characters to be normal
 punctuation, as for `conf-quote-normal'.  If this is `nil' then
 leave quote characters as quotes.  If this is a list, then
 consider the quote characters in the list to be normal
 punctuation.  If this is a single quote character, then consider
-that character only to be normal punctuation.")
+that character only to be normal punctuation."
+  :type '(choice boolean character (repeat character))
+  :safe 'mdw-conf-quote-normal-acceptable-value-p)
 (defun mdw-conf-quote-normal-acceptable-value-p (value)
   "Is the VALUE is an acceptable value for `mdw-conf-quote-normal'?"
   (or (booleanp value)
       (every (lambda (v) (memq v '(?\" ?')))
             (if (listp value) value (list value)))))
-(put 'mdw-conf-quote-normal 'safe-local-variable
-     'mdw-conf-quote-normal-acceptable-value-p)
 
 (defun mdw-fix-up-quote ()
   "Apply the setting of `mdw-conf-quote-normal'."
@@ -4525,8 +4880,13 @@ (defun mdw-fontify-lispy ()
 
 ;; Special indentation.
 
-(defvar mdw-lisp-loop-default-indent 2)
-(defvar mdw-lisp-setf-value-indent 2)
+(defcustom mdw-lisp-loop-default-indent 2
+  "Default indent for simple `loop' body."
+  :type 'integer
+  :safe 'integerp)
+(defcustom mdw-lisp-setf-value-indent 2
+  "Default extra indent for `setf' values."
+  :type 'integer :safe 'integerp)
 
 (setq lisp-simple-loop-indentation 0
       lisp-loop-keyword-indentation 0
@@ -4606,10 +4966,11 @@ (defadvice common-lisp-loop-part-indentation
                             (current-column))))
 
     ;; Don't really care about this.
-    (when (and (eq lisp-indent-backquote-substitution-mode 'corrected))
+    (when (and (boundp 'lisp-indent-backquote-substitution-mode)
+              (eq lisp-indent-backquote-substitution-mode 'corrected))
       (save-excursion
        (goto-char (elt state 1))
-       (cl-incf loop-indentation
+       (incf loop-indentation
                 (cond ((eq (char-before) ?,) -1)
                       ((and (eq (char-before) ?@)
                             (progn (backward-char)
@@ -4635,7 +4996,14 @@ (defadvice common-lisp-loop-part-indentation
 
       (setq ad-return-value
              (list
-              (cond ((not (lisp-extended-loop-p (elt state 1)))
+              (cond ((condition-case ()
+                         (save-excursion
+                           (goto-char (elt state 1))
+                           (forward-char 1)
+                           (forward-sexp 2)
+                           (backward-sexp 1)
+                           (not (looking-at "\\(:\\|\\sw\\)")))
+                       (error nil))
                      (+ baseline-indent lisp-simple-loop-indentation))
                     ((looking-at "^\\s-*\\(:?\\sw+\\|;\\)")
                      (+ baseline-indent lisp-loop-keyword-indentation))
@@ -4648,18 +5016,21 @@ (defadvice common-lisp-loop-part-indentation
 
 ;; SLIME setup.
 
-(defvar mdw-friendly-name "[mdw]"
-  "How I want to be addressed.")
+(defcustom mdw-friendly-name "[mdw]"
+  "How I want to be addressed."
+  :type 'string
+  :safe 'stringp)
 (defadvice slime-user-first-name
     (around mdw-use-friendly-name compile activate)
   (if mdw-friendly-name (setq ad-return-value mdw-friendly-name)
     ad-do-it))
 
-(trap
- (if (not mdw-fast-startup)
-     (progn
-       (require 'slime-autoloads)
-       (slime-setup '(slime-autodoc slime-c-p-c)))))
+(eval-and-compile
+  (trap
+    (if (not mdw-fast-startup)
+       (progn
+         (require 'slime-autoloads)
+         (slime-setup '(slime-autodoc slime-c-p-c))))))
 
 (let ((stuff '((cmucl ("cmucl"))
               (sbcl ("sbcl") :coding-system utf-8-unix)
@@ -4791,6 +5162,21 @@ (defun mdw-term-mode-setup ()
   (auto-fill-mode -1)
   (setq tab-width 8))
 
+(defun comint-send-and-indent ()
+  (interactive)
+  (comint-send-input)
+  (and mdw-auto-indent
+       (indent-for-tab-command)))
+
+(defadvice comint-line-beginning-position
+    (around mdw-calculate-it-properly () activate compile)
+  "Calculate the actual line start for multi-line input."
+  (if (or comint-use-prompt-regexp
+         (eq (field-at-pos (point)) 'output))
+      ad-do-it
+    (setq ad-return-value
+           (constrain-to-field (line-beginning-position) (point)))))
+
 (defun term-send-meta-right () (interactive) (term-send-raw-string "\e\e[C"))
 (defun term-send-meta-left  () (interactive) (term-send-raw-string "\e\e[D"))
 (defun term-send-ctrl-uscore () (interactive) (term-send-raw-string "\C-_"))
@@ -4834,9 +5220,10 @@ (defun ssh (host)
   (interactive "sHost: ")
   (ansi-term (list "ssh" host) (format "ssh@%s" host)))
 
-(defvar git-grep-command
+(defcustom git-grep-command
   "env GIT_PAGER=cat git grep --no-color -nH -e "
-  "*The default command for \\[git-grep].")
+  "The default command for \\[git-grep]."
+  :type 'string)
 
 (defvar git-grep-history nil)
 
@@ -4876,7 +5263,9 @@ (eval-after-load "magit"
                           magit-diff-refresh-popup
                           magit-diff-mode-refresh-popup
                           magit-revision-mode-refresh-popup))
-           (magit-define-popup-switch popup ?R "Reverse diff" "-R"))))
+           (magit-define-popup-switch popup ?R "Reverse diff" "-R"))
+         (magit-define-popup-switch 'magit-rebase-popup ?r
+                                    "Rebase merges" "--rebase-merges")))
 
 (defadvice magit-wip-commit-buffer-file
     (around mdw-just-this-buffer activate compile)
@@ -4929,6 +5318,35 @@ (defun mdw-try-smerge ()
       (smerge-mode 1))))
 (add-hook 'find-file-hook 'mdw-try-smerge t)
 
+(defcustom mdw-magit-new-window-modes
+  '(magit-diff-mode
+    magit-log-mode
+    magit-process-mode
+    magit-revision-mode
+    magit-stash-mode
+    magit-status-mode)
+  "Magit modes which should cause a new window to be used."
+  :type '(repeat symbol))
+
+(defun mdw-display-magit-buffer (buffer)
+  "Like `magit-display-buffer-traditional'.
+But uses `mdw-magit-new-window-modes' for its list of modes
+rather than baking the list into the function."
+  (display-buffer buffer
+                 (let ((mode (with-current-buffer buffer major-mode)))
+                   (if (and (not mdw-designated-window)
+                            (derived-mode-p 'magit-mode)
+                            (mdw-submode-p mode 'magit-mode)
+                            (not (memq mode mdw-magit-new-window-modes)))
+                       '(display-buffer-same-window . nil)
+                     nil))))
+(setq magit-display-buffer-function 'mdw-display-magit-buffer)
+
+(defun mdw-display-magit-file-buffer (buffer)
+  "Show a file buffer from a diff."
+  (select-window (display-buffer buffer)))
+(setq magit-display-file-buffer-function 'mdw-display-magit-file-buffer)
+
 ;;;--------------------------------------------------------------------------
 ;;; GUD, and especially GDB.
 
@@ -4942,6 +5360,15 @@ (defadvice gdb-set-window-buffer
   "Don't make windows dedicated.  Seriously."
   (set-window-dedicated-p (or window (selected-window)) nil))
 
+;;;--------------------------------------------------------------------------
+;;; SQL stuff.
+
+(setq sql-postgres-options '("-n" "-P" "pager=off")
+      sql-postgres-login-params
+       '((user :default "mdw")
+         (database :default "mdw")
+         (server :default "db.distorted.org.uk")))
+
 ;;;--------------------------------------------------------------------------
 ;;; Man pages.