2

I want to bind this function to M-w, so that it copies the region if one is selected, otherwise it copies the whole line silently, without having any other side-effects like moving the cursor to some other places.

I saw some examples, but they all do some additional stuff that I don't like, like moving the cursor to the next line.

I'm not an expert in Elisp, but some functions I saw might be buggy, because their bodies contain "\n", to refer to "newline", which is obviously wrong, because not all systems use this convention.

Drew
  • 77,472
  • 10
  • 114
  • 243
Bite Bytes
  • 291
  • 1
  • 8

2 Answers2

3

To avoid side effects like moving the point (that is, the cursor), wrap your movement commands in save-excursion. Prior Emacs 25 this also saved the mark (the other end of the selected region). Now to save the mark as well use save-mark-and-excursion.

First we need a function to select the whole line:

(defun mark-whole-line ()
  (beginning-of-line)
  (set-mark-command nil)
  (end-of-line))

This function moves the point and the mark.

Now we need to know if the region is active. The function region-active-p does this. Now just check the state of the region and use an if/then to either call the normal function M-w is bound to or save the state, use the above function to mark the whole line and then copy it:

(defun kill-ring-save-whole-line-or-region ()
  (interactive)
  (if (region-active-p)
      (call-interactively #'kill-ring-save) ;; then
    (save-mark-and-excursion ;; else
      (mark-whole-line)
      (kill-ring-save (region-beginning) (region-end))
      (pop-mark)
      )))

Finally to bind it to a key: (define-key global-map (kbd "M-w") #'kill-ring-save-whole-line-or-region)

NickD
  • 29,717
  • 3
  • 27
  • 44
erikstokes
  • 12,927
  • 2
  • 36
  • 56
  • I get the following error:

    Debugger entered--Lisp error: (error "Key sequence M - w starts with non-prefix key M")

    But when I use the following line it works properly:

    (global-set-key (kbd "M-w") 'kill-ring-save-whole-line-or-region)

    – Bite Bytes Jun 27 '20 at 14:38
  • 1
    @BiteBytes That's the part I typed without testing it. Fixed now. – erikstokes Jun 27 '20 at 14:42
  • FWIW, I have a similar command in misc-cmds.el. Putting it here just for info: (defun mark-line (&optional arg) "Put mark at end of line, point at beginning. A numeric prefix arg means move forward (backward if negative) that many lines, thus marking a line other than the one point was originally in." (interactive "P") (setq arg (if arg (prefix-numeric-value arg) 0)) (let ((inhibit-field-motion t)) (forward-line arg) (push-mark nil t t) (goto-char (line-end-position)))) – Drew Jun 27 '20 at 15:55
2

You want the whole-line-or-region package. With whole-line-or-region-local-mode activated, things like C-w and M-w Do The Right Thing on the current line when then is no active region.

NickD
  • 29,717
  • 3
  • 27
  • 44
Fran Burstall
  • 3,855
  • 11
  • 18