Skip to content

Instantly share code, notes, and snippets.

@mmarshall540
Last active June 14, 2025 15:12
Show Gist options
  • Save mmarshall540/8674f623084cefa733c0b76940f96f5d to your computer and use it in GitHub Desktop.
Save mmarshall540/8674f623084cefa733c0b76940f96f5d to your computer and use it in GitHub Desktop.
Making CUA-mode play nice
;; Emacs's CUA-mode is great for minimizing context-switching if
;; you're forced to also use "normal" programs.
;;
;; These are some settings that help CUA-mode play more nicely with
;; the rest of Emacs.
;;; Org-mode conflict-resolution
;; Avoid keybinding conflict between Org-mode and CUA-rectangle by
;; using "C-M-RET" instead of "C-RET" for the latter. This must be
;; set before enabling `cua-mode'.
(setopt cua-rectangle-mark-key [(control meta return)])
;;; Isearch convenience
(define-keymap
:keymap isearch-mode-map
;; Isearch doesn't know about CUA-mode. It only knows "C-y" if you
;; want to paste text into the search prompt. So this makes pasting
;; into the Isearch prompt work like the rest of CUA-mode.
"C-v" 'isearch-yank-kill
;; Isearch-mode-map binds the "C-x" prefix, which interferes with
;; CUA-mode when using Isearch to select text before killing. (See
;; https://emacs.stackexchange.com/questions/22621/cutting-selection-with-cua-mode-bindings-after-searching?rq=1).
;; We can move those keybindings to a custom "M-i" / "<insert>"
;; prefix (which is fleshed out more in the "Org-mode emphasis
;; convenience" section below).
"C-x" nil
"<insert> RET" 'isearch-char-by-name
"<insert> e RET" 'isearch-emoji-by-name
"M-i RET" 'isearch-char-by-name
"M-i e RET" 'isearch-emoji-by-name)
;;; Goto-address-mode conflict-resolution
;; This minor-mode binds "C-c RET" to `goto-address-at-point' in an
;; overlay-map. That can interfere with CUA-mode when copying text
;; with "C-c". If point is on or next to a URL, then the overlay
;; keymap overrides CUA-mode's keybinding, and we have difficulty
;; copying. So this removes the "C-c" prefix from the overlay's
;; keymap and binds the command to "RET" instead.
(with-eval-after-load 'goto-addr
(keymap-unset goto-address-highlight-keymap "C-c")
(keymap-set goto-address-highlight-keymap "RET" 'goto-address-at-point))
;;; Letter-case DWIM commands
;; These DWIM letter-case keybindings let us avoid typing
;; "C-x C-x C-u" or "C-x C-x C-l" just to `upcase-region' or
;; `downcase-region'. This code replaces the default `upcase-word',
;; `downcase-word', and `capitalize-word' keybindings, but without any
;; loss in functionality.
(define-keymap
:keymap global-map
"M-c" 'capitalize-dwim
"M-l" 'downcase-dwim
"M-u" 'upcase-dwim)
;;; Org-mode emphasis convenience
;; Instead of typing "C-c C-c C-x C-f" for `org-emphasize', add a
;; keymap to "M-i" and/or the "<insert>" key for inserting
;; emphasis-pairs around an active region. Also, consider adding the
;; other members of `insert-pair-alist' to this keymap.
(defvar-keymap my/insert-pair-map
"*" 'insert-pair
"+" 'insert-pair
"/" 'insert-pair
"=" 'insert-pair
"_" 'insert-pair
"~" 'insert-pair
"O" 'overwrite-mode ; "<insert> O" for the old "<insert>"
"M-i" 'tab-to-tab-stop) ; "M-i M-i" for the old "M-i"
(keymap-global-set "M-i" my/insert-pair-map)
(keymap-global-set "<insert>" my/insert-pair-map)
;; By default, `insert-pair-alist' only includes the pairs [], {}, <>,
;; "", '', `', and (). Any other pairs we want to insert using the
;; `insert-pair' command must be added to the list.
(add-to-list 'insert-pair-alist '(?* ?*))
(add-to-list 'insert-pair-alist '(?+ ?+))
(add-to-list 'insert-pair-alist '(?/ ?/))
(add-to-list 'insert-pair-alist '(?= ?=))
(add-to-list 'insert-pair-alist '(?_ ?_))
(add-to-list 'insert-pair-alist '(?~ ?~))
;;; Narrowing convenience
;; Instead of typing "C-x C-x n n" for narrowing to a region, make a
;; custom DWIM command, which can also narrow to other areas if the
;; region isn't active, or widen if the buffer was already narrowed.
(keymap-global-set "C-|" 'my/narrow-toggle) ; (mnemonic: narrow pipe)
;; Instead of typing "C-x C-x n n" for narrowing to a region, make a
;; custom DWIM command, which can also narrow to other areas if the
;; region isn't active, or widen if the buffer was already narrowed.
(defun my/narrow-toggle (arg)
"Toggle the buffer between narrowed and widened.
If widening, return point to its original position and scroll the window
to the same line it was at before narrowing (if the narrowing was
performed by this command). Also, ensure that if we are widening from a
narrowed page (e.g. after using the `pages-directory' command), that
there is a newline at the very end of the narrowed text to prevent
accidental mangling of page-dividers.
If narrowing, and the region is active, call `narrow-to-region'.
If it is inactive, and `org-mode' is the major-mode, call
`org-narrow-to-subtree', but if ARG is non-nil, call
`org-narrow-to-element' instead. If the major-mode is some other
derivative of `text-mode', then narrow to the paragraph.
Otherwise, narrow to the defun. Just like the `narrow-to-defun'
command, any comments preceding the defun will be included if a
prefix ARG is passed."
(interactive "P")
(if (buffer-narrowed-p)
;; widen and reposition at origin
(let* ((data (get 'my/narrow-toggle 'my/narrow-origin))
(nline (car data))
(nmark (cdr data))
(end (point-max)))
(widen)
;; Ensure page separators weren't mangled. If there is a "^L"
;; character immediately after the text that was narrowed,
;; make sure there is also a newline immediately before it.
(save-excursion
(goto-char end)
(when (and (looking-at (string 12))
(not (looking-back "\n" (1- (point)))))
(insert "\n")))
;; Go back to original location and reposition window to same
;; line as before.
(when data
(goto-char (marker-position nmark))
(recenter-top-bottom nline)
(put 'my/narrow-toggle 'my/narrow-origin nil)))
;; save original location before narrowing
(put 'my/narrow-toggle 'my/narrow-origin
(cons (cdr (nth 6 (posn-at-point))) (point-marker)))
(cond
((use-region-p)
;; narrow to region
(narrow-to-region (use-region-beginning) (use-region-end))
(deactivate-mark))
((eq major-mode 'org-mode)
(cond
((not arg)
;; narrow to org-subtree
(org-narrow-to-subtree)
(org-fold-show-subtree))
(arg
;; narrow to org-element
(org-narrow-to-element))))
((derived-mode-p 'text-mode)
;; narrow to paragraph
(save-excursion
(mark-paragraph)
(when (looking-at-p "[[:blank:]]*$") (forward-line))
(narrow-to-region (point) (mark)))
(deactivate-mark))
(t
;; narrow to the defun
(narrow-to-defun arg)))
(message (concat "Buffer narrowed, press "
(substitute-command-keys "\\[my/narrow-toggle]")
" or " (substitute-command-keys "\\[widen]")
" to see the whole buffer again."))))
@mmarshall540
Copy link
Author

Updated the my/narrow-toggle command to protect narrowed pages. I noticed that sometimes after narrowing to a page (done using C-x n p or with the pages-directory command) I would accidentally remove the newline at the end of the buffer, which is required for the page-delimiter to be recognized. It's easy to do that accidentally, since you can't see the next form-feed character when the page is narrowed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment