]> git.eshelyaron.com Git - emacs.git/commitdiff
New command 'elisp-extract'
authorEshel Yaron <me@eshelyaron.com>
Tue, 14 Jan 2025 23:06:13 +0000 (00:06 +0100)
committerEshel Yaron <me@eshelyaron.com>
Tue, 14 Jan 2025 23:06:13 +0000 (00:06 +0100)
lisp/progmodes/elisp-mode.el
lisp/progmodes/refactor-elisp.el
lisp/progmodes/which-func.el

index 82c92593d2ad5bd3527ae4a31eabbacffcf8417d..c3f7deff603363966cf3dba29cbab7dea18248f7 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-x" #'elisp-extract
   "C-c C-n" #'elisp-next-occurrence
   "C-c C-p" #'elisp-prev-occurrence)
 
@@ -2366,6 +2367,39 @@ interactively, this is the prefix argument."
   "C-n" #'elisp-next-occurrence
   "C-p" #'elisp-prev-occurrence)
 
+(defun elisp-extract (beg end new)
+  "Extract region from BEG to END into NEW function."
+  ;; FIXME:
+  ;; - Check that region is a valid form.
+  ;; - Handle Local function bindings (e.g. `named-let').
+  ;; - Group buffer changes together.
+  ;; - Order bound variables by their binding positions.
+  (interactive
+   (if (use-region-p)
+       (list (use-region-beginning)
+             (use-region-end)
+             (let ((def (when-let ((d (which-function))) (concat d "-1"))))
+               (read-string (format-prompt "Function name" def) nil nil def)))
+     (user-error "No region selected"))
+   emacs-lisp-mode)
+  (let* (bound-vars)
+    (save-excursion
+      (goto-char beg)
+      (beginning-of-defun)
+      (scope (lambda (_type sbeg len bin)
+               (and (<= beg sbeg (+ sbeg len) end) (numberp bin) (< bin beg)
+                    (cl-pushnew (intern (buffer-substring sbeg (+ sbeg len)))
+                                bound-vars))))
+      (insert "\n\n(defun " new " (" (mapconcat #'symbol-name bound-vars " ") ")" "\n")
+      (indent-according-to-mode)
+      (push-mark nil t)
+      (insert (string-trim (buffer-substring beg end)) ")")
+      (prog-indent-sexp 'defun)
+      (goto-char beg)
+      (delete-region beg end)
+      (insert "(" new " " (mapconcat #'symbol-name bound-vars " ") ")")
+      (prog-indent-sexp 'defun))))
+
 \f
 (put 'read-symbol-shorthands 'safe-local-variable #'consp)
 
index 10ecab64925af62ceb4ea1740e5c016c933f3a2b..737c0707dd0d00956f4fb5da5565fc0c1cf271e1 100644 (file)
@@ -41,8 +41,7 @@
                      (throw 'var-def
                             (list (propertize
                                    (buffer-substring-no-properties beg (+ beg len))
-                                   'pos beg)))))
-                 (current-buffer))
+                                   'pos beg))))))
           nil)))))
 
 (cl-defmethod refactor-backend-rename-edits
@@ -55,8 +54,7 @@
       (scope (lambda (_type beg len bin)
                (when (<= beg pos (+ beg len))
                  (setq dec bin))
-               (when bin (push (list beg len bin) all)))
-             (current-buffer)))
+               (when bin (push (list beg len bin) all)))))
     (list
      (cons (current-buffer)
            (let (res)
@@ -75,8 +73,7 @@
       (scope (lambda (_type beg len bin)
                (when (<= beg pos (+ beg len))
                  (setq dec bin))
-               (when bin (push (list beg len bin) all)))
-             (current-buffer)))
+               (when bin (push (list beg len bin) all)))))
     (when dec
       (let (res)
         (pcase-dolist (`(,beg ,len ,bin) all)
index 7bd1bf44ebcf9a2205b8ca1173a9ecc43b778f51..eb3c0d51b08a13b66fa2cf285c0895e993b635ae 100644 (file)
@@ -321,6 +321,7 @@ in certain major modes."
 It calls them sequentially, and if any returns non-nil,
 `which-function' uses that name and stops looking for the name.")
 
+;;;###autoload
 (defun which-function ()
   "Return current function name based on point.
 Uses `which-func-functions', `add-log-current-defun'.