rolandwalker / button-lock

clickable text in Emacs
42 stars 6 forks source link

Using regexp groups in the action #10

Open jkitchin opened 6 years ago

jkitchin commented 6 years ago

I find myself doing something like this a lot lately. I have a regexp with some groups in it that match text I want to make a button of, but in the action I only need to use part of the text. For example below will make a #hashtag into a button, but in the action I only need the "hashtag", e.g. to open the hashtag on Twitter or something.

;; The hashtag is in group 1.
(setq hashtag-regexp "\\(^\\|[[:space:]]\\|\\s(\\)\\(?2:#\\(?1:[[:alnum:]]*\\)\\)")

(button-lock-set-button
 hashtag-regexp
 (lambda ()
   (interactive)
   (goto-char (1- (previous-single-property-change (point) 'button-lock)))
   (save-match-data
     (looking-at hashtag-regexp)
     (message (match-string-no-properties 1))))
 :grouping 2
 :additional-property 'button-lock
 :face (list 'link)
 :help-echo "Click me to open the hashtag.")

The most robust way I have found so far is via property searching to get to the beginning of the match, and then "look at it" again. Is this the right approach to doing this?

rolandwalker commented 6 years ago

Hi!

Indeed, that's the right approach, since when the callback happens, its knowledge is pretty much limited to the fact that the user clicked a mouse-button.

Button-lock buttons can respond to clicks other than mouse-1, so the value of (point) doesn't always change in tandem with the click. Thus consulting the mouse event directly is more general. So I usually write the above more like

(setq hashtag-regexp "\\(^\\|[[:space:]]\\|\\s(\\)\\(?2:#\\(?1:[[:alnum:]]*\\)\\)")

(defun hashtag-mouse-action (event)
  "Callback for clicks on hashtags."
  (interactive "e")
  (let ((click-pos (posn-point (event-end event))))
    (save-mark-and-excursion
     (save-match-data
       (goto-char (1- (previous-single-property-change click-pos 'hashtag)))
       (when (looking-at hashtag-regexp)
         (message "%s" (match-string-no-properties 1)))))))

(setq hashtag-button
      (button-lock-set-button hashtag-regexp
                              #'hashtag-mouse-action
                              :grouping 2
                              :additional-property 'hashtag      ; custom property
                              :face (list 'link)
                              :help-echo "Click me to open the hashtag."))

;; (button-lock-unset-button hashtag-button)

(The regular expression confused me at first, perhaps because it was simplified for the sake of the example? It could be more like \\(?:^\\|[[:space:]]\\|\\s(\\)\\(#\\([[:alnum:]]+\\)\\), reversing the group numbers.)

jkitchin commented 6 years ago

Thanks for the confirmation, and improvement.

The regexp is just something that worked. The ?2: syntax just lets you number the groups the way you want. Once groups get nested, and there are many of them, I find it confusing to remember/figure out which group I need, so I just number them explicitly.