diff --git a/lisp/imenu.el b/lisp/imenu.el index 2636e77d08..69338d216a 100644 --- a/lisp/imenu.el +++ b/lisp/imenu.el @@ -52,6 +52,7 @@ ;;; Code: (require 'cl-lib) +(require 'xref) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; @@ -473,8 +474,10 @@ imenu--create-keymap (if cmd (funcall cmd item) item)))))) alist))) -(defun imenu--in-alist (str alist) - "Check whether the string STR is contained in multi-level ALIST." +(defun imenu--in-alist (str alist &optional all) + "Check whether the string STR is contained in multi-level ALIST. +If the optional argument ALL is non-nil, then return all matches +of STR in ALIST." (let (elt head tail res) (setq res nil) (while alist @@ -491,12 +494,18 @@ imenu--in-alist ;; We are only interested in the bottom-level elements, so we need to ;; recurse if TAIL is a nested ALIST. (cond ((imenu--subalist-p elt) - (if (setq res (imenu--in-alist str tail)) - (setq alist nil))) + (let ((r (imenu--in-alist str tail all))) + (if all + (setq res (append res (if (listp (cdr r)) r (list r)))) + (setq res r) + (when r + (setq alist nil))))) ((if imenu-name-lookup-function (funcall imenu-name-lookup-function str head) (string= str head)) - (setq alist nil res elt)))) + (if all + (push elt res) + (setq alist nil res elt))))) res)) ;;;###autoload @@ -550,6 +559,61 @@ imenu-default-create-index-function (t (imenu-unavailable-error "This buffer cannot use `imenu-default-create-index-function'")))) +;;; +;;; Xref backend +;;; + +;;;###autoload +(defun imenu-xref-backend () + (unless imenu--index-alist + (imenu--make-index-alist)) + (when (and imenu--index-alist + (not (progn (require 'etags) tags-table-files))) + 'imenu)) + +(cl-defmethod xref-backend-identifier-at-point ((_backend (eql 'imenu))) + (thing-at-point 'symbol)) + +(defun imenu-xref--make-summary (marker) + (with-current-buffer (marker-buffer marker) + (save-excursion + (goto-char marker) + (back-to-indentation) + (buffer-substring (point) (point-at-eol))))) + +(cl-defmethod xref-backend-definitions ((_backend (eql 'imenu)) symbol) + (let ((res (imenu--in-alist symbol imenu--index-alist t)) + defs) + (dolist (item res) + (push (xref-make (imenu-xref--make-summary (cdr item)) + (xref-make-buffer-location (marker-buffer (cdr item)) + (marker-position (cdr item)))) + defs)) + defs)) + +(cl-defmethod xref-backend-identifier-completion-ignore-case ((_backend (eql 'imenu))) + imenu-case-fold-search) + +(defun imenu-xref--make-affixations (alist &optional prefix) + (let (res) + (dolist (item alist) + (if (imenu--subalist-p item) + (setq res (append res (imenu-xref--make-affixations + (cdr item) + (concat prefix (when prefix imenu-level-separator) (car item))))) + (push (list (car item) (concat prefix (when prefix imenu-level-separator)) "") res))) + res)) + +(cl-defmethod xref-backend-identifier-completion-table ((_backend (eql 'imenu))) + (let ((affixations (imenu-xref--make-affixations imenu--index-alist))) + (lambda (string pred action) + (if (eq action 'metadata) + `(metadata (affixation-function + . ,(lambda (cmp) (mapcar (lambda (c) (assoc c affixations #'equal)) cmp)))) + ;; This works since (car AFFIXATIONS) is the completion + ;; candidate. + (complete-with-action action affixations string pred))))) + ;;; ;;; Generic index gathering function. ;;;