]> git.eshelyaron.com Git - emacs.git/commitdiff
New command 'elisp-extract-local-variable'
authorEshel Yaron <me@eshelyaron.com>
Thu, 16 Jan 2025 20:07:27 +0000 (21:07 +0100)
committerEshel Yaron <me@eshelyaron.com>
Thu, 16 Jan 2025 20:07:27 +0000 (21:07 +0100)
lisp/progmodes/elisp-mode.el

index 82c92593d2ad5bd3527ae4a31eabbacffcf8417d..ad65e0700aa8e47d5f30abacb6d4493a86b13bd7 100644 (file)
@@ -58,6 +58,7 @@ All commands in `lisp-mode-shared-map' are inherited by this map."
   "C-c C-b" #'elisp-byte-compile-buffer
   "C-M-q" #'indent-pp-sexp
   "C-c M-e" #'macrostep-expand
+  "C-c C-l" #'elisp-extract-to-local-variable
   "C-c C-n" #'elisp-next-occurrence
   "C-c C-p" #'elisp-prev-occurrence)
 
@@ -2366,6 +2367,63 @@ interactively, this is the prefix argument."
   "C-n" #'elisp-next-occurrence
   "C-p" #'elisp-prev-occurrence)
 
+(defvar avy-action)
+
+(defvar elisp-extract-local-variable-name-history nil)
+
+(defun elisp-extract-to-local-variable (beg end target var)
+  "Extract BEG to END region to local VAR bound at TARGET."
+  (interactive
+   (let ((beg (region-beginning))
+         (end (region-end))
+         (max 0)
+         (targets nil))
+     (save-excursion
+       (goto-char beg)
+       (beginning-of-defun-raw)
+       (scope (lambda (type sbeg len bin)
+                (and (<= sbeg beg)
+                     (memq type '(function macro special-form top-level))
+                     (push (save-excursion (nth 1 (syntax-ppss sbeg))) targets))
+                (let ((send (+ sbeg len)))
+                  (and (<= beg sbeg send end) (numberp bin) (< bin beg)
+                       (setq max (max max bin)))))))
+     (let* ((target
+             (if-let ((avy-action #'ignore)
+                      (targets (seq-drop-while
+                                (apply-partially #'> max)
+                                (sort (seq-intersection
+                                       (nth 9 (syntax-ppss)) targets #'=)))))
+                 (or (avy-process targets) (keyboard-quit))
+               (user-error "No valid targets")))
+            (tarend (save-excursion (goto-char target) (scan-sexps (point) 1)))
+            (ovbeg (make-overlay target (1+ target)))
+            (ovend (make-overlay tarend (1+ tarend))))
+       (overlay-put ovbeg 'face 'show-paren-match)
+       (overlay-put ovend 'face 'show-paren-match)
+       (list beg end target
+             (unwind-protect
+                 (read-string (format-prompt "Extract region to local var called" "v")
+                              nil 'elisp-extract-local-variable-name-history "v")
+               (delete-overlay ovbeg)
+               (delete-overlay ovend)))))
+   emacs-lisp-mode)
+  (let ((reg (delete-and-extract-region beg end)))
+    (goto-char beg)
+    (insert var)
+    (let ((pos (copy-marker (point))))
+      (goto-char target)
+      (pcase (save-excursion (read (current-buffer)))
+        (`(,(or 'let 'let* 'if-let* 'when-let* 'while-let*) . ,_)
+         (down-list 2)
+         (insert "(" var " " reg ")\n"))
+        (_
+         (insert "(let ((" var " " reg "))\n")
+         (goto-char (scan-sexps (point) 1))
+         (insert ")")))
+      (prog-indent-sexp 'defun)
+      (goto-char pos))))
+
 \f
 (put 'read-symbol-shorthands 'safe-local-variable #'consp)