diff options
| author | Stephen Leake | 2015-08-10 21:53:19 -0500 |
|---|---|---|
| committer | Stephen Leake | 2015-08-10 21:55:37 -0500 |
| commit | d7df36e745a5ba480559b6c8b5ebc93a18fe9bd1 (patch) | |
| tree | 736918633a929b4f88e871509b699f9a00dcf398 /lisp | |
| parent | 21e1673be3738fb79bd92cf8bd003d86ac51130b (diff) | |
| download | emacs-d7df36e745a5ba480559b6c8b5ebc93a18fe9bd1.tar.gz emacs-d7df36e745a5ba480559b6c8b5ebc93a18fe9bd1.zip | |
Rewrite elisp--xref-find-definitions to handle many more cases; add tests.
* lisp/progmodes/elisp-mode.el (elisp--xref-identifier-location): deleted
(elisp--xref-format-cl-defmethod): new
(find-feature-regexp): new
(find-alias-regexp): new
(elisp--xref-make-xref): new
(elisp--xref-find-definitions): Rewrite using the above, handle many more
cases. Always output all available definitions.
(xref-location-marker): No need for special cases.
* test/automated/elisp-mode-tests.el: Add more tests of
elisp--xref-find-definitions, improve current tests.
Diffstat (limited to 'lisp')
| -rw-r--r-- | lisp/progmodes/elisp-mode.el | 168 |
1 files changed, 113 insertions, 55 deletions
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index b7ae3c756de..41ca57f668d 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | 28 | ||
| 29 | ;;; Code: | 29 | ;;; Code: |
| 30 | 30 | ||
| 31 | (require 'cl-generic) | ||
| 31 | (require 'lisp-mode) | 32 | (require 'lisp-mode) |
| 32 | (eval-when-compile (require 'cl-lib)) | 33 | (eval-when-compile (require 'cl-lib)) |
| 33 | 34 | ||
| @@ -441,6 +442,7 @@ It can be quoted, or be inside a quoted form." | |||
| 441 | (string-match ".*$" doc) | 442 | (string-match ".*$" doc) |
| 442 | (match-string 0 doc)))) | 443 | (match-string 0 doc)))) |
| 443 | 444 | ||
| 445 | ;; can't (require 'find-func) in a preloaded file | ||
| 444 | (declare-function find-library-name "find-func" (library)) | 446 | (declare-function find-library-name "find-func" (library)) |
| 445 | (declare-function find-function-library "find-func" (function &optional l-o v)) | 447 | (declare-function find-function-library "find-func" (function &optional l-o v)) |
| 446 | 448 | ||
| @@ -598,60 +600,122 @@ It can be quoted, or be inside a quoted form." | |||
| 598 | (`apropos | 600 | (`apropos |
| 599 | (elisp--xref-find-apropos id)))) | 601 | (elisp--xref-find-apropos id)))) |
| 600 | 602 | ||
| 601 | (defun elisp--xref-identifier-location (type sym) | 603 | (defconst elisp--xref-format |
| 602 | (let ((file | ||
| 603 | (pcase type | ||
| 604 | (`defun (when (fboundp sym) | ||
| 605 | (let ((fun-lib | ||
| 606 | (find-function-library sym))) | ||
| 607 | (setq sym (car fun-lib)) | ||
| 608 | (cdr fun-lib)))) | ||
| 609 | (`defvar (and (boundp sym) | ||
| 610 | (let ((el-file (symbol-file sym 'defvar))) | ||
| 611 | (if el-file | ||
| 612 | (and | ||
| 613 | ;; Don't show minor modes twice. | ||
| 614 | ;; TODO: If TYPE ever becomes dependent on the | ||
| 615 | ;; context, move this check outside. | ||
| 616 | (not (and (fboundp sym) | ||
| 617 | (memq sym minor-mode-list))) | ||
| 618 | el-file) | ||
| 619 | (help-C-file-name sym 'var))))) | ||
| 620 | (`feature (and (featurep sym) | ||
| 621 | ;; Skip when a function with the same name | ||
| 622 | ;; is defined, because it's probably in the | ||
| 623 | ;; same file. | ||
| 624 | (not (fboundp sym)) | ||
| 625 | (ignore-errors | ||
| 626 | (find-library-name (symbol-name sym))))) | ||
| 627 | (`defface (when (facep sym) | ||
| 628 | (symbol-file sym 'defface)))))) | ||
| 629 | (when file | ||
| 630 | (when (string-match-p "\\.elc\\'" file) | ||
| 631 | (setq file (substring file 0 -1))) | ||
| 632 | (xref-make-elisp-location sym type file)))) | ||
| 633 | |||
| 634 | (defvar elisp--xref-format | ||
| 635 | (let ((str "(%s %s)")) | 604 | (let ((str "(%s %s)")) |
| 636 | (put-text-property 1 3 'face 'font-lock-keyword-face str) | 605 | (put-text-property 1 3 'face 'font-lock-keyword-face str) |
| 637 | (put-text-property 4 6 'face 'font-lock-function-name-face str) | 606 | (put-text-property 4 6 'face 'font-lock-function-name-face str) |
| 638 | str)) | 607 | str)) |
| 639 | 608 | ||
| 609 | (defconst elisp--xref-format-cl-defmethod | ||
| 610 | (let ((str "(%s %s %s)")) | ||
| 611 | (put-text-property 1 3 'face 'font-lock-keyword-face str) | ||
| 612 | (put-text-property 4 6 'face 'font-lock-function-name-face str) | ||
| 613 | str)) | ||
| 614 | |||
| 615 | (defcustom find-feature-regexp | ||
| 616 | (concat "(provide +'%s)") | ||
| 617 | "The regexp used by `xref-find-definitions' to search for a feature definition. | ||
| 618 | Note it must contain a `%s' at the place where `format' | ||
| 619 | should insert the feature name." | ||
| 620 | :type 'regexp | ||
| 621 | :group 'xref | ||
| 622 | :version "25.0") | ||
| 623 | |||
| 624 | (defcustom find-alias-regexp | ||
| 625 | "(\\(defalias +'\\|def\\(const\\|face\\) +\\)%s" | ||
| 626 | "The regexp used by `xref-find-definitions' to search for an alias definition. | ||
| 627 | Note it must contain a `%s' at the place where `format' | ||
| 628 | should insert the feature name." | ||
| 629 | :type 'regexp | ||
| 630 | :group 'xref | ||
| 631 | :version "25.0") | ||
| 632 | |||
| 633 | (with-eval-after-load 'find-func | ||
| 634 | (defvar find-function-regexp-alist) | ||
| 635 | (add-to-list 'find-function-regexp-alist (cons 'feature 'find-feature-regexp)) | ||
| 636 | (add-to-list 'find-function-regexp-alist (cons 'defalias 'find-alias-regexp))) | ||
| 637 | |||
| 638 | (defun elisp--xref-make-xref (type symbol file &optional summary) | ||
| 639 | "Return an xref for TYPE SYMBOL in FILE. | ||
| 640 | TYPE must be a type in 'find-function-regexp-alist' (use nil for | ||
| 641 | 'defun). If SUMMARY is non-nil, use it for the summary; | ||
| 642 | otherwise build the summary from TYPE and SYMBOL." | ||
| 643 | (xref-make (or summary | ||
| 644 | (format elisp--xref-format (or type 'defun) symbol)) | ||
| 645 | (xref-make-elisp-location symbol type file))) | ||
| 646 | |||
| 640 | (defun elisp--xref-find-definitions (symbol) | 647 | (defun elisp--xref-find-definitions (symbol) |
| 641 | (save-excursion | 648 | ;; The file name is not known when `symbol' is defined via interactive eval. |
| 642 | (let (lst) | 649 | (let (xrefs) |
| 643 | (dolist (type '(feature defface defvar defun)) | 650 | ;; alphabetical by result type symbol |
| 644 | (let ((loc | 651 | |
| 645 | (condition-case err | 652 | ;; FIXME: advised function; list of advice functions |
| 646 | (elisp--xref-identifier-location type symbol) | 653 | |
| 647 | (error | 654 | ;; FIXME: aliased variable |
| 648 | (xref-make-bogus-location (error-message-string err)))))) | 655 | |
| 649 | (when loc | 656 | (when (and (symbolp symbol) |
| 650 | (push | 657 | (symbol-function symbol) |
| 651 | (xref-make (format elisp--xref-format type symbol) | 658 | (symbolp (symbol-function symbol))) |
| 652 | loc) | 659 | ;; aliased function |
| 653 | lst)))) | 660 | (let* ((alias-symbol symbol) |
| 654 | lst))) | 661 | (alias-file (symbol-file alias-symbol)) |
| 662 | (real-symbol (symbol-function symbol)) | ||
| 663 | (real-file (find-lisp-object-file-name real-symbol 'defun))) | ||
| 664 | |||
| 665 | (when real-file | ||
| 666 | (push (elisp--xref-make-xref nil real-symbol real-file) xrefs)) | ||
| 667 | |||
| 668 | (when alias-file | ||
| 669 | (push (elisp--xref-make-xref 'defalias alias-symbol alias-file) xrefs)))) | ||
| 670 | |||
| 671 | (when (facep symbol) | ||
| 672 | (let ((file (find-lisp-object-file-name symbol 'defface))) | ||
| 673 | (when file | ||
| 674 | (push (elisp--xref-make-xref 'defface symbol file) xrefs)))) | ||
| 675 | |||
| 676 | (when (fboundp symbol) | ||
| 677 | (let ((file (find-lisp-object-file-name symbol (symbol-function symbol))) | ||
| 678 | generic) | ||
| 679 | (when file | ||
| 680 | (cond | ||
| 681 | ((eq file 'C-source) | ||
| 682 | ;; First call to find-lisp-object-file-name (for this | ||
| 683 | ;; symbol?); C-source has not been cached yet. | ||
| 684 | ;; Second call will return "src/*.c" in file; handled by 't' case below. | ||
| 685 | (push (elisp--xref-make-xref nil symbol (help-C-file-name (symbol-function symbol) 'subr)) xrefs)) | ||
| 686 | |||
| 687 | ((setq generic (cl--generic symbol)) | ||
| 688 | (dolist (method (cl--generic-method-table generic)) | ||
| 689 | (let* ((info (cl--generic-method-info method)) | ||
| 690 | (met-name (cons symbol (cl--generic-method-specializers method))) | ||
| 691 | (descr (format elisp--xref-format-cl-defmethod 'cl-defmethod symbol (nth 1 info))) | ||
| 692 | (file (find-lisp-object-file-name met-name 'cl-defmethod))) | ||
| 693 | (when file | ||
| 694 | (push (elisp--xref-make-xref 'cl-defmethod met-name file descr) xrefs)) | ||
| 695 | )) | ||
| 696 | |||
| 697 | (let ((descr (format elisp--xref-format 'cl-defgeneric symbol))) | ||
| 698 | (push (elisp--xref-make-xref nil symbol file descr) xrefs)) | ||
| 699 | ) | ||
| 700 | |||
| 701 | (t | ||
| 702 | (push (elisp--xref-make-xref nil symbol file) xrefs)) | ||
| 703 | )))) | ||
| 704 | |||
| 705 | (when (boundp symbol) | ||
| 706 | (let ((file (find-lisp-object-file-name symbol 'defvar))) | ||
| 707 | (when file | ||
| 708 | (when (eq file 'C-source) | ||
| 709 | (setq file (help-C-file-name symbol 'var))) | ||
| 710 | (push (elisp--xref-make-xref 'defvar symbol file) xrefs)))) | ||
| 711 | |||
| 712 | (when (featurep symbol) | ||
| 713 | (let ((file (ignore-errors | ||
| 714 | (find-library-name (symbol-name symbol))))) | ||
| 715 | (when file | ||
| 716 | (push (elisp--xref-make-xref 'feature symbol file) xrefs)))) | ||
| 717 | |||
| 718 | xrefs)) | ||
| 655 | 719 | ||
| 656 | (declare-function project-search-path "project") | 720 | (declare-function project-search-path "project") |
| 657 | (declare-function project-current "project") | 721 | (declare-function project-current "project") |
| @@ -689,13 +753,7 @@ It can be quoted, or be inside a quoted form." | |||
| 689 | 753 | ||
| 690 | (cl-defmethod xref-location-marker ((l xref-elisp-location)) | 754 | (cl-defmethod xref-location-marker ((l xref-elisp-location)) |
| 691 | (pcase-let (((cl-struct xref-elisp-location symbol type file) l)) | 755 | (pcase-let (((cl-struct xref-elisp-location symbol type file) l)) |
| 692 | (let ((buffer-point | 756 | (let ((buffer-point (find-function-search-for-symbol symbol type file))) |
| 693 | (pcase type | ||
| 694 | (`defun (find-function-search-for-symbol symbol nil file)) | ||
| 695 | ((or `defvar `defface) | ||
| 696 | (find-function-search-for-symbol symbol type file)) | ||
| 697 | (`feature | ||
| 698 | (cons (find-file-noselect file) 1))))) | ||
| 699 | (with-current-buffer (car buffer-point) | 757 | (with-current-buffer (car buffer-point) |
| 700 | (goto-char (or (cdr buffer-point) (point-min))) | 758 | (goto-char (or (cdr buffer-point) (point-min))) |
| 701 | (point-marker))))) | 759 | (point-marker))))) |