Skip to content

Instantly share code, notes, and snippets.

@rytswd
Created September 30, 2023 22:55
Show Gist options
  • Save rytswd/f169d5acadd8a7e688075c1bb983f915 to your computer and use it in GitHub Desktop.
Save rytswd/f169d5acadd8a7e688075c1bb983f915 to your computer and use it in GitHub Desktop.
Emacs: Duplicate line or region like VSCode
(defun rytswd/duplicate-line-or-region-up (arg)
"Duplicate the current line or selected region upward."
(interactive "*p")
(dotimes (_ arg)
(rytswd/duplicate-line-or-region -1)))
(defun rytswd/duplicate-line-or-region-down (arg)
"Duplicate the current line or selected region downward."
(interactive "*p")
(dotimes (_ arg)
(rytswd/duplicate-line-or-region 1)))
(defun rytswd/duplicate-line-or-region (n)
"Duplicate the current line or selected region upward or downward. The
direction is determined by the argument n being positive (i.e. downward)
or negative (i.e. upward). When a region is in play, the region will be kept
in a way so that this duplicate command can be replayed multiple times."
(let* ((is-region (use-region-p))
(beg (if is-region (region-beginning) nil))
(end (if is-region (region-end) nil))
(start (point))
(region-other-point (if is-region (if (eq start beg) end beg)))
(line-start (if is-region
(save-mark-and-excursion
(goto-char beg)
(line-beginning-position))
(line-beginning-position)))
(line-end (if is-region
(save-mark-and-excursion
(goto-char end)
(line-end-position))
(line-end-position)))
(text (concat (buffer-substring line-start line-end) "\n"))
(forward (if (< 0 n) 1 -1))
(shift (length text)))
;; If duplication direction is down (forward), keep the existing lines as
;; they appear, and insert before the current line. This makes it look as
;; if the selection has moved down, and can be used to keep duplicating
;; the selected lines downwards.
(if (> forward 0)
;; Because other content will be inserted, save-mark-and-excursion won't
;; work. For that reason, I need to do some manual mark adjustment.
(progn (goto-char line-start)
(insert text)
(goto-char (+ start shift))
(when is-region
(save-excursion
(push-mark (+ region-other-point shift) 'nomsg nil)))
)
;; If duplication direction is up, insert the item below the current
;; line. This allows upward duplication to be repeated.
(progn (goto-char line-end)
(forward-line 1)
(insert text)
(goto-char start)
(when is-region
(save-excursion
(push-mark region-other-point 'nomsg nil)))
)
)
(setq deactivate-mark nil)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment