7d.nz

Inserting characters pairs

[2020-04-24 Fri]

  code elisp

/ Update [2022-09-18 Sun] The function will now emphasize previous word when at the end of it (you can type a word, then emphasize it) or the current word (e.g. when the cursor is over it)./

An easy way to emphasize or enclose words or region with pairs matching : “”, «», [], and parentheses of course. Unbreakable spaces are also inserted around where the typographical convention demands it. Better than org-emphasize.

In my book, the org formatting becomes, when the cursor is at the beginning of word or over a region:

C-= *

bold

C-= /

italic

C-= _

underline

C-= (

(parentheses)

C-= “

“ quotes ”

And I must say there is much confort in doing so. The binding to C-= can be discussed.

The code:

  (add-to-list 'insert-pair-alist '(171 187)) ;; « »
(add-to-list 'insert-pair-alist '(8220 8221)) ;; “ ”
(setq insert-non-breakable-space-with-pair-list '(171 8220)) ;; « ... »

(defun insert-character-pair (&optional arg)

  "Interactively calls insert-pair, with the next
   (not last) typed character.
   If `parens-require-spaces' is non-nil, this
   command also inserts a space before and after,
   depending on the surrounding characters.
   Look into `insert-non-breakable-space-with-pair-list'
   for the opening character of a pair wich needs the
   insertion of a non-breakable space in-between.
   Typically « quotes ».
   If region is active, insert enclosing characters
   at region boundaries.

  [2022-01-02 Sun] did something changed with the
   point position after insert-pair ?  "

  (interactive "c")
  (let* ((pair (assq arg insert-pair-alist))
        (space-required parens-require-spaces)
        (n (prefix-numeric-value current-prefix-arg))
	(syntax-prev (syntax-class (syntax-after (- (point) 1))))
	(syntax-next (syntax-class (syntax-after (point))))
	(previous-char-is-whitespace (and syntax-prev (= 0 syntax-prev)))
	(next-char-is-whitespace (and syntax-next (= 0 syntax-next))))
    
    ;; begining of word. surround next word
    ;; middle or end of word. surround current word
    (setq insert-direction (if previous-char-is-whitespace 1 -1))

    (if pair
      (setq open (car pair)
            close (car(cdr pair)))
      (setq open arg
            close arg)) 
    ;;(save-mark-and-excursion
      (if (memq open insert-non-breakable-space-with-pair-list)
          (if (and transient-mark-mode mark-active)
              (save-mark-and-excursion
                (when (> (point) (mark)) (exchange-point-and-mark))
                (insert-pair 1 open close)
                (save-mark-and-excursion
                  (goto-char (mark))
                  (insert 160)) ;; insert nbsp
                (goto-char (point))
                (insert 160))
	    ;; else
            (progn
              (save-excursion (insert-pair insert-direction open close))
	      (when (= 1 insert-direction) (right-char))
              (setq parens-require-spaces nil)
              (save-excursion (insert-pair insert-direction 160 160))
              (setq parens-require-spaces space-required)))
	
        (progn
          ;; (when (and transient-mark-mode mark-active (> (point) (mark)))
	  ;;   (exchange-point-and-mark))

          (save-mark-and-excursion
            (dotimes (i n) (insert-pair 
			    insert-direction
			    open close)))
	  (if (> (point) (mark))
	    (right-char))
          ))
      ;; go past the emphasize character added
      ;; avoiding the rabbit-hold that position the mark after the word,
      ;; but before the emphasize marker
      ;; and indiscernable when `org-hide-emphasis-markers' is t)
      (when (= (char-after (point)) close)
	(right-char))
      )
  )
(global-set-key (kbd "C-=") 'insert-character-pair)

Perhaps somebody already wrote this function and there is certainly rooms for improvement. It took me several attempt to write it down correctly and I learned 100% of the elisp functions invocated while doing so. I hope it will be of any use to you dear reader.

Current known limitations