EmacsWiki: Whole Line Or Region (original) (raw)

In many programs, like SlickEdit, TextMate and VisualStudio, “cut” and “copy” act on the current line if no text is visually selected. This page says how to get the same behavior in Emacs, so that

Such behaviour for ‘C-w’ is available through the command kill-whole-line, bound to ‘C-S-backspace’. Note: Transient Mark Mode must be enabled to distinguish selected text visually – that is, (transient-mark-mode t) – which is the default in Emacs 23 and onwards.

The `interactive-form' symbol property

Use the function put to alter the interactive form of kill-ring-save and kill-region (Emacs’ default copy and cut commands):

(put 'kill-ring-save 'interactive-form
 '(interactive
   (if (use-region-p)
       (list (region-beginning) (region-end))
     (list (line-beginning-position) (line-beginning-position 2)))))

(put 'kill-region 'interactive-form      
 '(interactive
   (if (use-region-p)
       (list (region-beginning) (region-end))
     (list (line-beginning-position) (line-beginning-position 2)))))

Thus, ‘M-w’ with no selection copies the current line, ‘C-w’ kills it entirely, and ‘C-a M-w C-y’ duplicates it. As the interactive-form property only affects the commands’ interactive behavior, they are safe for other functions to call.

For emacs versions >= 24.3, the above code for altering kill-ring-save does not work (see http://lists.gnu.org/archive/html/help-gnu-emacs/2013-03/msg00261.html). Instead, use this:

(defun my-kill-ring-save (beg end flash)
  (interactive (if (use-region-p)
                   (list (region-beginning) (region-end) nil)
                 (list (line-beginning-position)
                       (line-beginning-position 2) 'flash)))
  (kill-ring-save beg end)
  (when flash
    (save-excursion
      (if (equal (current-column) 0)
          (goto-char end)
        (goto-char beg))
      (sit-for blink-matching-delay))))
(global-set-key [remap kill-ring-save] 'my-kill-ring-save)

If you use slime-repl-mode (a mode for interacting with an external REPL), replace (line-beginning-position) with

(if (eq major-mode 'slime-repl-mode)
    slime-repl-input-start-mark
  (line-beginning-position))

You could easily have this behaviour for all commands that operate on the region, if you so wish:

(do-all-symbols (symbol)
  (when (and (commandp symbol t)
             (string-match-p "-region$\\|kill-ring-save" (symbol-name symbol)))
    (put symbol 'interactive-form
         '(interactive
           (if (use-region-p)
               (list (region-beginning) (region-end))
             (list (line-beginning-position) (line-beginning-position 2)))))))

With yank-handler

A further keypress can be saved by using the yank-handler text property. The following will paste a copied line above the current line regardless of the position of point:

(defadvice kill-ring-save (around slick-copy activate)
  "When called interactively with no active region, copy a single line instead."
  (if (or (use-region-p) (not (called-interactively-p)))
      ad-do-it
    (kill-new (buffer-substring (line-beginning-position)
                                (line-beginning-position 2))
              nil '(yank-line))
    (message "Copied line")))

(defadvice kill-region (around slick-copy activate)
  "When called interactively with no active region, kill a single line instead."
  (if (or (use-region-p) (not (called-interactively-p)))
      ad-do-it
    (kill-new (filter-buffer-substring (line-beginning-position)
                                       (line-beginning-position 2) t)
              nil '(yank-line))))

(defun yank-line (string)
  "Insert STRING above the current line."
  (beginning-of-line)
  (unless (= (elt string (1- (length string))) ?\n)
    (save-excursion (insert "\n")))
  (insert string))

Thus, a line can be duplicated with ‘M-w C-y’.

whole-line-or-region.el is a minor mode which allows functions to operate on the current line when they would normally operate on the region. When yanking, it’s smart enough to know that the string to be yanked was killed as a whole line and should be yanked as such, so one doesn’t need to position oneself at the start of the line. It also accepts prefix arguments.

Furthermore, it preserves the position of point. If killing the first line of the following text:

The GNU Project was launched in 1984 to develop a complete
Unix-like operating system which is free software: the GNU system.

And if point is positioned as such when yanking the killed text:

Unix-like operating system wh>|<ich is free software: the GNU system.

One winds up with:

The GNU Project was launched in 1984 to develop a complete
Unix-like operating system wh>|<ich is free software: the GNU system.

In addition, the package provides a framework for enabling any region-based command (e.g., comment-dwim) to work on the current line if region is undefined.

The function whole-line-or-region-kill-region doesn’t respect the kill-read-only-ok variable (it always signals an error when the buffer is read-only). Here is some advice to fix that:

(defadvice whole-line-or-region-kill-region
           (before whole-line-or-region-kill-read-only-ok activate)
  (interactive "p")
  (unless kill-read-only-ok (barf-if-buffer-read-only)))

See also CopyingWholeLines.


CategoryRegion