Skip to content

Instantly share code, notes, and snippets.

@jdtsmith
Last active February 15, 2024 11:27
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jdtsmith/55e6a660dd4c0779a600ac81bf9bfc23 to your computer and use it in GitHub Desktop.
Save jdtsmith/55e6a660dd4c0779a600ac81bf9bfc23 to your computer and use it in GitHub Desktop.
org-toggle-emphasis: easily toggle emphasis markers: =~*/_+
(defun my/org-toggle-emphasis (type)
"Toggle org emphasis TYPE (a character) at point."
(cl-labels ((in-emph (re)
"See if in org emphasis given by RE."
(and (org-in-regexp re 2)
(>= (point) (match-beginning 3))
(<= (point) (match-end 4))))
(de-emphasize ()
"Remove most recently matched org emphasis markers."
(save-excursion
(replace-match "" nil nil nil 3)
(delete-region (match-end 4) (1+ (match-end 4))))))
(let* ((res (vector org-emph-re org-verbatim-re))
(idx (cl-case type (?/ 0) (?* 0) (?_ 0) (?+ 0) (?= 1) (?~ 1)))
(re (aref res idx))
(other-re (aref res (- 1 idx)))
(type-re (string-replace (if (= idx 1) "=~" "*/_+")
(char-to-string type) re))
add-bounds offset is-word)
(save-match-data
(if (region-active-p)
(if (in-emph type-re) (de-emphasize) (org-emphasize type))
(if (eq (char-before) type) (backward-char))
(if (in-emph type-re) ;nothing marked, in emph text?
(de-emphasize)
(setq add-bounds ; check other flavors
(if (or (in-emph re) (in-emph other-re))
(cons (match-beginning 4) (match-end 4))
(setq is-word t)
(bounds-of-thing-at-point 'symbol))))
(if add-bounds
(let ((off (- (point) (car add-bounds)))
(at-end (= (point) (cdr add-bounds))))
(set-mark (car add-bounds))
(goto-char (cdr add-bounds))
(org-emphasize type) ;deletes marked region!
(unless is-word ; delete extra spaces
(goto-char (car add-bounds))
(when (eq (char-after) ?\s) (delete-char 1))
(goto-char (+ 2 (cdr add-bounds)))
(when (eq (char-after) ?\s) (delete-char 1)))
(goto-char (+ (car add-bounds) off
(cond ((= off 0) 0) (at-end 2) (t 1)))))
(if is-word (org-emphasize type))))))))
@jeff-phil
Copy link

Great! Thanks for sharing, as always. Hope feedback is welcome.

May consider using (bounds-of-thing-at-point 'symbol) instead of 'word, so works on hyphenated words and vari-abl-es.

Also, have you seen this snippet for insert-pair that has been floating around for a while? Can include the extra chars with:

(add-to-list 'insert-pair-alist '((?\* ?\*) ; org bold
                                  (?\/ ?\/) ; org italic
                                  (?\= ?\=) ; org verbatim
                                  (?\~ ?\~) ; org code
                                  (?\_ ?\_) ; org underline
                                  (?\+ ?\+) ; org strike-thru
                                  (?\` ?\`))) ; markdown code

Or if calling insert-pair direct, something like this for insert or delete is similar (obviously not as complete, i.e. determining if region-active-p) for your idea:

(defun my/org-toggle-emphasis (&optional type)
  (interactive)
  (save-mark-and-excursion
    (goto-char (car (bounds-of-thing-at-point 'symbol)))
    (if type
        (insert-pair 1 type type)
      (delete-pair))))

(keymap-global-set "s-i" '("italicize" . (lambda () (interactive) (my/org-toggle-emphasis ?/)))
(keymap-global-set "s-d" '("delete-emphasis" . (lambda () (interactive) (my/org-toggle-emphasis)))

@jdtsmith
Copy link
Author

jdtsmith commented Jan 15, 2024

May consider using (bounds-of-thing-at-point 'symbol) instead of 'word, so works on hyphenated words and vari-abl-es.

Thanks! I had in fact wanted this, and reached for current-word, which does the right thing, but that returns the word and not its bounds. Even considered submitting a patch for current-word-bounds.

Also, have you seen this snippet for insert-pair

Nope, I hadn't seen that interesting approach, thanks. I tend to just use s-e when at a blank to get a ~~ with point between. That's what the final (if is-word (org-emphasize type)) does. I.e. it was a word, but we didn't find one, so just emphasize "nothing".

@jdtsmith
Copy link
Author

Just tweaked to correctly de-emphasize right after an emphasized word *word*[s-b] -> word, and to leave point outside the word if point is on the boundary to begin with.

@jeff-phil
Copy link

I had in fact wanted this, and reached for current-word, which does the right thing

Interesting, I thought current-word 'word etc. all used the same underlying syntax tables to define a word. Guess not! Thanks for that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment