Last active
May 31, 2024 11:13
-
-
Save dandrake/864f642850acaa3534cf5029868d12eb to your computer and use it in GitHub Desktop.
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
;; Inspired by this blog post on "let's surround": | |
;; https://arialdomartini.github.io/emacs-surround-2 and | |
;; https://www.emacswiki.org/emacs/SurroundRegion. | |
;; | |
;; Notable bits: | |
;; | |
;; - Specifying =open= as a default value in the closing delimiter completing | |
;; read -- that allows the user to hit return to re-use the opening | |
;; delimiter for the closing one. | |
;; - This moves point accordingly, so that you can make repeated calls to | |
;; the function (using `C-x M-:` or similar) and it will correctly nest | |
;; the surrounded region. | |
;; - I don't know if it's just a vertico thing, but =completing-read= is | |
;; pretty aggressive about, well, completing based on the history. If you | |
;; type in something that has a completion candidate, and you want to use | |
;; what you typed and not the candidate, you need to use =M-RET=. | |
(defvar surround-region-opening-delim-hist nil "Completing read history for surround-region's opening delimiters.") | |
(defvar surround-region-closing-delim-hist nil "Completing read history for surround-region's closing delimiters.") | |
(defun surround-region-with-delimiters (opening-delimiter closing-delimiter beginning-position ending-position) | |
"Insert OPENING-DELIMITER at BEGINNING-POSITION and CLOSING-DELIMITER at ENDING-POSITION." | |
(with-current-buffer (current-buffer) | |
(goto-char ending-position) | |
(insert closing-delimiter) | |
(goto-char beginning-position) | |
(insert opening-delimiter) | |
(if (> (point) (mark)) | |
;; selecting text forwards: put point at end of newly-inserted closing delim | |
(goto-char (+ ending-position (length opening-delimiter) (length closing-delimiter))) | |
(progn | |
;; else, selecting backwards: put point at start of opening delim, | |
;; mark at end of newly-inserted closing delim | |
(set-mark (+ ending-position (length opening-delimiter) (length closing-delimiter))) | |
(goto-char beginning-position))))) | |
(defun surround-region () | |
"Calls `surround-region-with-delimiters' when the buffer is not read-only. | |
If the closing delimiter is the empty string, re-uses the opening delimiter. | |
Note that with vertico completion, use `M-<return>' (`vertico-exit-input') | |
to use the currently-entered string as-is, not the currently highlighted | |
completion candidate." | |
(interactive "*") | |
(let* ((open (completing-read "Opening delimiter: " | |
surround-region-opening-delim-hist nil nil nil | |
'surround-region-opening-delim-hist)) | |
(close (completing-read "Closing delimiter (blank to use opening delimiter): " | |
surround-region-closing-delim-hist nil nil nil | |
'surround-region-closing-delim-hist open))) | |
(surround-region-with-delimiters open close (region-beginning) (region-end)))) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment