diff --git a/easy-kill.el b/easy-kill.el index c31af0d..1b2d3bc 100644 --- a/easy-kill.el +++ b/easy-kill.el @@ -40,6 +40,7 @@ ;;; Code: + (require 'cl-lib) (require 'thingatpt) (require 'gv nil t) ;For `defsetf'. @@ -123,6 +124,8 @@ deprecated." (let ((map (make-sparse-keymap))) (define-key map "-" 'easy-kill-shrink) (define-key map "+" 'easy-kill-expand) + (define-key map "[" 'easy-kill-shrink-backward-edge) + (define-key map "]" 'easy-kill-expand-backward-edge) (define-key map "=" 'easy-kill-expand) (define-key map " " 'easy-kill-cycle) (define-key map "@" 'easy-kill-append) @@ -364,10 +367,10 @@ candidate property instead." (interprogram-paste-function nil)) (kill-new (if (and (easy-kill-get append) kill-ring) (cl-labels ((join (x sep y) - (if sep (concat (easy-kill-trim x 'right) - sep - (easy-kill-trim y 'left)) - (concat x y)))) + (if sep (concat (easy-kill-trim x 'right) + sep + (easy-kill-trim y 'left)) + (concat x y)))) (join (car kill-ring) (nth 2 (cl-rassoc (easy-kill-get thing) easy-kill-alist :key #'car)) @@ -393,6 +396,10 @@ candidate property instead." (interactive) (easy-kill-thing nil '+)) +(defun easy-kill-expand-backward-edge () + (interactive) + (easy-kill-thing nil '+ nil 'backward)) + (defun easy-kill-cycle (&optional thing) "Cycle through things in `easy-kill-alist'. A thing is opted out of cycling if in `easy-kill-cycle-ignored'." @@ -433,6 +440,10 @@ expansion." (interactive) (easy-kill-thing nil '-)) +(defun easy-kill-shrink-backward-edge () + (interactive) + (easy-kill-thing nil '- nil 'backward)) + (defun easy-kill-thing-handler (base mode) "Get the handler for MODE or nil if none is defined. For example, if BASE is \"easy-kill-on-list\" and MODE is @@ -455,10 +466,10 @@ checked." ;; `thing-at-point-bounds-of-url-at-point' that could return a ;; boundary not containing current point. (cl-flet ((chk (bound) - (pcase-let ((`(,b . ,e) bound)) - (and b e - (<= b (point)) (<= (point) e) - (cons b e))))) + (pcase-let ((`(,b . ,e) bound)) + (and b e + (<= b (point)) (<= (point) e) + (cons b e))))) (pcase (easy-kill-thing-handler (format "easy-kill-bounds-of-%s-at-point" thing) major-mode) @@ -474,32 +485,51 @@ checked." (_ (forward-thing thing n)))) ;; Helper for `easy-kill-thing'. -(defun easy-kill-thing-forward (n) + +;; which-edge: +;; nil or 'forward is the forward edge +;; (the right-most one on RTL text) +;; 'backward is the backward edge +;; (the left-most one on RTL text) +;; forward/backward is defined according to https://www.gnu.org/software/emacs/manual/html_node/emacs/Bidirectional-Editing.html +;; +(defun easy-kill-thing-forward (n &optional which-edge) (when (and (easy-kill-get thing) (/= n 0)) (let* ((step (if (cl-minusp n) -1 +1)) (thing (easy-kill-get thing)) (bounds1 (or (easy-kill-pair-to-list (easy-kill-bounds-of-thing-at-point thing)) (list (point) (point)))) + (origin-thing-start (car bounds1)) + (origin-thing-end (car (last bounds1))) (start (easy-kill-get start)) (end (easy-kill-get end)) - (front (or (car (cl-set-difference (list end start) bounds1)) - (pcase step - (`-1 start) - (`1 end)))) - (new-front (save-excursion - (goto-char front) - (with-demoted-errors - (dotimes (_ (abs n)) - (easy-kill-thing-forward-1 thing step))) - (point)))) + (front (cond + ( (eq which-edge 'backward) + start) + ( t + end))) + (new-front (save-excursion + (goto-char front) + (with-demoted-errors + (dotimes (_ (abs n)) + (easy-kill-thing-forward-1 thing step))) + (point)))) (pcase (and (/= front new-front) - (sort (cons new-front bounds1) #'<)) - (`(,start ,_ ,end) - (easy-kill-adjust-candidate thing start end) - t))))) - -(defun easy-kill-thing (&optional thing n inhibit-handler) + (cond + ( (eq which-edge 'backward) + (if (cl-minusp n) + (list new-front end)) + (list (min new-front origin-thing-start) end )) + ( (eq which-edge nil) + (if (cl-minusp n) + (list start (max new-front origin-thing-end )) + (list start new-front))))) + (`(,start ,end) + (easy-kill-adjust-candidate thing start end) + t) ) ) ) ) + +(defun easy-kill-thing (&optional thing n inhibit-handler which-edge) ;; N can be -, + and digits (interactive (list (pcase (assq last-command-event easy-kill-alist) @@ -521,13 +551,15 @@ checked." (easy-kill-thing-forward (pcase n (`+ 1) (`- -1) - (_ n)))) + (_ n)) + which-edge + )) (t (pcase (easy-kill-bounds-of-thing-at-point thing) (`nil (easy-kill-echo "No `%s'" thing)) (`(,start . ,end) (easy-kill-adjust-candidate thing start end) (unless (zerop n) - (easy-kill-thing-forward (1- n))))))) + (easy-kill-thing-forward (1- n) which-edge)))))) (when (easy-kill-get mark) (easy-kill-adjust-candidate (easy-kill-get thing))))) @@ -690,11 +722,11 @@ inspected." (if (or (easy-kill-get mark) (easy-kill-bounds-of-thing-at-point 'url)) (easy-kill-thing 'url nil t) (cl-labels ((get-url (text) - (when (stringp text) - (with-temp-buffer - (insert text) - (pcase (easy-kill-bounds-of-thing-at-point 'url) - (`(,beg . ,end) (buffer-substring beg end))))))) + (when (stringp text) + (with-temp-buffer + (insert text) + (pcase (easy-kill-bounds-of-thing-at-point 'url) + (`(,beg . ,end) (buffer-substring beg end))))))) (cl-dolist (p '(help-echo shr-url w3m-href-anchor)) (pcase (get-char-property-and-overlay (point) p) (`(,text . ,ov)