Emacs: extend-selection, select-text-in-quote, select-current-line, select-current-block

Master emacs+lisp, benefit for life. Testimonials. Thank you for support.
, , …,

Emacs has a command to select the current word, mark-word, with default shortcut of 【Alt+@】. Selecting the current word is a frequently needed operation. However, emacs's mark-word command is inconvenient. It does not select the whole word. It only select from the cursor position to the end of the word. For example, if your word is “transmission”, and your cursor is at the “m”, then it will select just “mission”. To select the whole word, you need to move the cursor to the beginning of the word first.

Also, mark-word has a feature that if you repeat the command, then it extend the selection to the next word to the right. But again, you need first to move the cursor to the start position you want. Emacs has in fact a whole system of selecting text:

“M-@”
     Set mark after end of next word (“mark-word”).  This command and
     the following one do not move point.

“C-M-@”
     Set mark after end of following balanced expression (“mark-sexp”).

“M-h”
     Put region around current paragraph (“mark-paragraph”).

“C-M-h”
     Put region around current defun (“mark-defun”).

“C-x h”
     Put region around the entire buffer (“mark-whole-buffer”).

“C-x C-p”
     Put region around current page (“mark-page”).

(info "(emacs) Marking Objects")

Here is suggestion of a scheme that may replace or supplement the above system.

We create a command that select the whole word. When repeated, it should select the next larger syntactic unit. In human languages, that would be sentence, then paragraph, then whole buffer. In computer languages, the sequence would be: current identifier or token, current expression, current construct (or line), current block or defun. If the language is lisp, this simply means extending the selection to the next outer parens. For a illustrated example of this, see: A Text Editor Feature: Extend Selection By Semantic Unit.

extend-selection

Here's the code that implements the above idea for lisp (or any simply nested syntax):

;; by Nikolaj Schumacher, 2008-10-20. Released under GPL.
(defun semnav-up (arg)
  (interactive "p")
  (when (nth 3 (syntax-ppss))
    (if (> arg 0)
        (progn
          (skip-syntax-forward "^\"")
          (goto-char (1+ (point)))
          (decf arg))
      (skip-syntax-backward "^\"")
      (goto-char (1- (point)))
      (incf arg)))
  (up-list arg))

;; by Nikolaj Schumacher, 2008-10-20. Released under GPL.
(defun extend-selection (arg &optional incremental)
  "Select the current word.
Subsequent calls expands the selection to larger semantic unit."
  (interactive (list (prefix-numeric-value current-prefix-arg)
                     (or (use-region-p)
                         (eq last-command this-command))))
  (if incremental
      (progn
        (semnav-up (- arg))
        (forward-sexp)
        (mark-sexp -1))
    (if (> arg 1)
        (extend-selection (1- arg) t)
      (if (looking-at "\\=\\(\\s_\\|\\sw\\)*\\_>")
          (goto-char (match-end 0))
        (unless (memq (char-before) '(?\) ?\"))
          (forward-sexp)))
      (mark-sexp -1))))

(global-set-key (kbd "M-8") 'extend-selection)

We suggest 【Alt+8】 for the command. Pressing it once will select the current whole word. Press it again will extend the selection to the next outer parens. The above code effectively does extend selection to higher level of semantic unit for lisp or simply nested syntax. It does not work in more complicated nesting, such as HTML/XML. For the code to work in other languages like Java, Perl, Python, XML, it'll need some more work.

Another frequently needed operation is to select text inside straight 'single' or "double" quotes. This is especially frequently needed for string datatype in popular languages such as C, C++, Java, JavaScript, Perl, Python, PHP, HTML/XML.

The “extend-selection” above can be used to select quoted text. If the quoted text has more than one word, you need to call it twice. Also, “extend-selection” will select including the quote.

Select Text in Quote

Following is a dedicated command that select just text inside quotes.

(defun xah-select-text-in-quote ()
  "Select text between ASCII quotes, single or double."
  (interactive)
  (let (p1 p2)
    (if (nth 3 (syntax-ppss))
        (progn
          (backward-up-list 1 "ESCAPE-STRINGS" "NO-SYNTAX-CROSSING")
          (setq p1 (point))
          (forward-sexp 1)
          (setq p2 (point))
          (goto-char (1+ p1))
          (set-mark (1- p2)))
      (progn
        (error "Cursor not inside quote")))))

Select Text in Bracket

(defun xah-select-text-in-bracket ()
  "Select text between the nearest brackets.
⁖  () [] {} «» ‹› “” 〖〗 【】 「」 『』 () 〈〉 《》 〔〕 ⦗⦘ 〘〙 ⦅⦆ 〚〛 ⦃⦄ ⟨⟩."
  (interactive)
  (with-syntax-table (standard-syntax-table)
    (modify-syntax-entry ?\« "(»")
    (modify-syntax-entry ?\» ")«")
    (modify-syntax-entry ?\‹ "(›")
    (modify-syntax-entry ?\› ")‹")
    (modify-syntax-entry ?\“ "(”")
    (modify-syntax-entry ?\” ")“")
    (modify-syntax-entry ?\‘ "(’")
    (modify-syntax-entry ?\’ ")‘")
    (let (pos p1 p2)
      (setq pos (point))
      (search-backward-regexp "\\s(" nil t )
      (setq p1 (point))
      (forward-sexp 1)
      (setq p2 (point))
      (goto-char (1+ p1))
      (set-mark (1- p2)))))

Select Text in Bracket or Quote

(defun xah-select-text-in-bracket-or-quote ()
  "Select text between the nearest brackets or quote."
  (interactive)
  (if (nth 3 (syntax-ppss))
        (xah-select-text-in-quote)
      (xah-select-text-in-bracket)))

You should give this command a key. 〔☛ Emacs: How to Define Keys〕.

Select Current Line

A “line” is a frequent semantic unit in source code. Here's a command that select current line by a single operation.

(defun xah-select-current-line ()
  "Select the current line."
  (interactive)
  (end-of-line)
  (set-mark (line-beginning-position)))

Select Current Block

select-current-block lets you select current block of text by a single keystroke. (a block here is text between empty lines. It's similar to emacs's “paragraph” concept, except it's not major-mode dependent, so the behavior is predictable anywhere.)

(defun xah-select-current-block ()
  "Select the current block of text between blank lines."
  (interactive)
  (let (p1 p2)
    (progn
      (if (re-search-backward "\n[ \t]*\n" nil "move")
          (progn (re-search-forward "\n[ \t]*\n")
                 (setq p1 (point)))
        (setq p1 (point)))
      (if (re-search-forward "\n[ \t]*\n" nil "move")
          (progn (re-search-backward "\n[ \t]*\n")
                 (setq p2 (point)))
        (setq p2 (point))))
    (set-mark p1)))

This extend-selection scheme with transient-mark-mode on, should simplify and replace the functionality of {mark-word, mark-sexp, mark-paragraph, mark-defun}. With just one command to remember, and more efficient to operate. The mark-whole-buffer can be replaced by a shortcut 【Ctrl+a】 to be compatible with modern UI standard, and mark-page is today rather obsolete because it depends on page marker char “^L” (ASCII 12), which is not used in most languages but only in older emacs lisp source code.

The above suggestions are implemented in Ergoemacs at ErgoEmacs Keybinding.

These commands are most useful together with commands that move cursor to brackets or quotes. 〔☛ Emacs: Commands to Move Cursor by Brackets, Quotes

Like what you read?
Buy Xah Emacs Tutorial
or share some
blog comments powered by Disqus