abo-abo / avy

Jump to things in Emacs tree-style
1.73k stars 110 forks source link

[FR] Select two arbitrary characters and copy the content between them #367

Open NightMachinery opened 1 year ago

NightMachinery commented 1 year ago

I want to easily copy text in the style of evil-avy-goto-char. This is particularly useful in magit, where normal evil bindings don't work. If we could have avy-copy-from-char-to-char, this would solve the problem.

NightMachinery commented 1 year ago

I wrote this myself with a little help from GPT4. (it's amazing how GPT4 actually knows the internal avy functions.)

(cl-defun night/read-char-or-cancel
    (&optional prompt inherit-input-method seconds
               &key (cancel-chars '(27)))
  "Read a character from the user, but cancel if one of CANCEL-CHARS is pressed.
CANCEL-CHARS is a list of characters that will trigger a cancellation.
By default, it contains only the escape character (27)."
  (let ((char (read-char prompt inherit-input-method seconds)))
    (if (memq char cancel-chars)
        (progn
          (message "Cancelled!")
          nil)
      char)))

(defun night/avy-copy-from-char-to-char ()
    "Copy text between two characters using avy."
    ;; [[https://github.com/abo-abo/avy/issues/367][{FR} Select two arbitrary characters and copy the content between them · Issue #367 · abo-abo/avy]]
;;;
    (interactive)
    ;; Get the starting position
    (save-excursion
      (when-let ((start-char (night/read-char-or-cancel "Start char: ")))
        ;; (message "start-char: %s" start-char)
        (let (start-pos end-pos)
          (setq start-pos (avy-process (avy--regex-candidates (string start-char))))
          ;; If a starting position is selected
          (when (and
                 start-pos
                 (listp start-pos))
            (when-let ((end-char (night/read-char-or-cancel "End char: ")))
              (setq end-pos (avy-process (avy--regex-candidates (string end-char))))
              ;; If an ending position is selected, copy the text
              (when (and
                     end-pos
                     (listp end-pos))
                ;; (message "start: %s, end: %s" start-pos end-pos)
                ;; start: (3417 . 3418), end: (3419 . 3420)
                (let ((start-pos (car start-pos))
                      (end-pos (cdr end-pos)))
                  (kill-ring-save
                   start-pos
                   end-pos)
                  (nav-flash-show start-pos end-pos nil 1)))))))))