Last active
May 25, 2025 18:36
-
-
Save mmarshall540/8674f623084cefa733c0b76940f96f5d to your computer and use it in GitHub Desktop.
Making CUA-mode play nice
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; 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) | |
(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). | |
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))) | |
(widen) | |
(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.")))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment