]> git.eshelyaron.com Git - emacs.git/commitdiff
New commands 'elisp-next/prev-occurrence'
authorEshel Yaron <me@eshelyaron.com>
Fri, 27 Dec 2024 15:26:40 +0000 (16:26 +0100)
committerEshel Yaron <me@eshelyaron.com>
Fri, 27 Dec 2024 15:26:40 +0000 (16:26 +0100)
lisp/progmodes/elisp-mode.el

index 4003628dcb0437e49866e74e92868dce58a4fe9d..d5a28f0041ea9d1cd7b6f0699cbb77cbebcc6209 100644 (file)
@@ -57,7 +57,9 @@ All commands in `lisp-mode-shared-map' are inherited by this map."
   "C-c C-f" #'elisp-byte-compile-file
   "C-c C-b" #'elisp-byte-compile-buffer
   "C-M-q" #'indent-pp-sexp
-  "C-c M-e" #'macrostep-expand)
+  "C-c M-e" #'macrostep-expand
+  "C-c C-n" #'elisp-next-occurrence
+  "C-c C-p" #'elisp-prev-occurrence)
 
 (easy-menu-define emacs-lisp-mode-menu emacs-lisp-mode-map
   "Menu for Emacs Lisp mode."
@@ -1033,34 +1035,39 @@ namespace but with lower confidence."
                     (elisp--xref-filter-definitions defs 'any sym))
               (elisp--xref-filter-definitions defs namespace sym))))))))
 
+(defun elisp-local-references (pos)
+  "Return references to local variable at POS as (BEG . LEN) cons cells."
+  (let (all cur)
+    (save-excursion
+      (goto-char pos)
+      (beginning-of-defun)
+      (scope (lambda (_type beg len bin)
+               (when (<= beg pos (+ beg len))
+                 (setq cur bin))
+               (when bin (setf (alist-get beg all) (list len bin))))
+             (current-buffer)))
+    (seq-keep
+     (pcase-lambda (`(,sym ,len ,bin)) (when (equal bin cur) (cons sym len)))
+     all)))
+
 (cl-defmethod xref-backend-references :around ((backend (eql 'elisp)) identifier)
   (let* ((pos (get-text-property 0 'pos identifier))
-         all dec)
-    (when pos
-      (save-excursion
-        (goto-char pos)
-        (beginning-of-defun)
-        (scope (lambda (_type beg len bin)
-                 (when (<= beg pos (+ beg len))
-                   (setq dec bin))
-                 (when bin (setf (alist-get beg all) (list len bin))))
-               (current-buffer))))
-    (if dec
+         (all (elisp-local-references pos)))
+    (if all
         (let (res)
-          (pcase-dolist (`(,sym ,len ,bin) all)
-            (when (equal bin dec)
-              (let* ((beg-end (save-excursion
-                                (goto-char sym)
-                                (cons (pos-bol) (pos-eol))))
-                     (beg (car beg-end))
-                     (end (cdr beg-end))
-                     (line (buffer-substring-no-properties beg end))
-                     (cur (- sym beg)))
-                (add-face-text-property cur (+ len cur)
-                                        'xref-match t line)
-                (push (xref-make line (xref-make-buffer-location
-                                       (current-buffer) sym))
-                      res))))
+          (pcase-dolist (`(,sym . ,len) all)
+            (let* ((beg-end (save-excursion
+                              (goto-char sym)
+                              (cons (pos-bol) (pos-eol))))
+                   (beg (car beg-end))
+                   (end (cdr beg-end))
+                   (line (buffer-substring-no-properties beg end))
+                   (cur (- sym beg)))
+              (add-face-text-property cur (+ len cur)
+                                      'xref-match t line)
+              (push (xref-make line (xref-make-buffer-location
+                                     (current-buffer) sym))
+                    res)))
           res)
       (cl-call-next-method backend identifier))))
 
@@ -2278,7 +2285,7 @@ Otherwise, call `eval-buffer'."
   "Byte compile the file the current buffer is visiting.
 If LOAD is non-nil, load the resulting .elc file.  When called
 interactively, this is the prefix argument."
-  (interactive "P")
+  (interactive "P" elisp-mode)
   (unless buffer-file-name
     (error "This buffer is not visiting a file"))
   (byte-compile-file buffer-file-name)
@@ -2289,7 +2296,7 @@ interactively, this is the prefix argument."
   "Byte compile the current buffer, but don't write a file.
 If LOAD is non-nil, load byte-compiled data.  When called
 interactively, this is the prefix argument."
-  (interactive "P")
+  (interactive "P" elisp-mode)
   (let ((bfn buffer-file-name)
         file elc)
     (require 'bytecomp)
@@ -2305,7 +2312,7 @@ interactively, this is the prefix argument."
                 (byte-compile-log-warning-function
                  (lambda (string position &optional fill level)
                    (if bfn
-                       ;; Massage the warnings to that they point to
+                       ;; Massage the warnings so that they point to
                        ;; this file, not the one in /tmp.
                        (let ((byte-compile-current-file bfn)
                              (byte-compile-root-dir (file-name-directory bfn)))
@@ -2329,6 +2336,36 @@ interactively, this is the prefix argument."
         (when (file-exists-p elc)
           (delete-file elc))))))
 
+(defun elisp-prev-occurrence (n)
+  "Go to previous Nth occurrence of local variable at point."
+  (interactive "p" emacs-lisp-mode)
+  (elisp-next-occurrence (- n)))
+
+(defun elisp-next-occurrence (n)
+  "Go to next Nth occurrence of local variable at point."
+  (interactive "p" emacs-lisp-mode)
+  (if-let ((pos (point))
+           (next (nth (1- (abs n))
+                      (sort (seq-filter (pcase-lambda (`(,beg . ,_))
+                                          (and (eq (< pos beg) (< 0 n))
+                                               (not (= pos beg))))
+                                        (or (elisp-local-references pos)
+                                            (user-error "No local variable at point")))
+                            :reverse (< n 0))))
+           (beg (car next))
+           (end (+ beg (cdr next))))
+      (progn
+        (goto-char beg)
+        (pulse-momentary-highlight-region beg end 'highlight)
+        (when elisp-next-occurrence-repeat-map
+          (set-transient-map elisp-next-occurrence-repeat-map)))
+    (user-error "No next occurrence")))
+
+(defvar-keymap elisp-next-occurrence-repeat-map
+  :doc "Repeat keymap for `elisp-next-occurrence' and `elisp-prev-occurrence'."
+  "C-n" #'elisp-next-occurrence
+  "C-p" #'elisp-prev-occurrence)
+
 \f
 (put 'read-symbol-shorthands 'safe-local-variable #'consp)