summaryrefslogtreecommitdiff
path: root/lisp/citre-backend.el
diff options
context:
space:
mode:
authorcashmere <cashmere1337@riseup.net>2026-09-23 00:10:34 +0200
committercashmere <cashmere1337@riseup.net>2026-09-23 00:10:34 +0200
commit4b4af818d1e95449b635cc15b92f946c3d023edd (patch)
treeccb909be04216be98fe32fccda8599ba4542f0a1 /lisp/citre-backend.el
parentbf6a1d2dbc6147166272eca0276443779012e925 (diff)
downloademacs-4b4af818d1e95449b635cc15b92f946c3d023edd.tar.gz
relic: 2026-09-23T00:10:38+02:00
Diffstat (limited to 'lisp/citre-backend.el')
-rw-r--r--lisp/citre-backend.el59
1 files changed, 52 insertions, 7 deletions
diff --git a/lisp/citre-backend.el b/lisp/citre-backend.el
index eefdd31..9169d31 100644
--- a/lisp/citre-backend.el
+++ b/lisp/citre-backend.el
@@ -31,6 +31,12 @@ The value `safe' filters by an explicitly declared receiver type only when at le
:type '(set (const scope) (const source) (const count))
:group 'my/citre)
+(defcustom my/citre-reference-program "rg"
+ "Program used to search references.
+It must accept ripgrep's options and honour the project's ignore files."
+ :type 'string
+ :group 'my/citre)
+
(defcustom my/citre-completion-type-resolvers
'(my/citre-backend--resolve-go-type
my/citre-backend--resolve-rust-or-python-type
@@ -248,14 +254,14 @@ Each function receives a receiver name and the beginning of its member expressio
(let* ((type (plist-get context :type))
(matching (and type (seq-filter (lambda (tag) (my/citre-backend--scope-match-p type tag)) tags)))
(visible (if (and (eq my/citre-completion-context-filter 'safe) matching) matching tags))
- (groups (make-hash-table :test #'equal))
(current-scope (my/citre-backend--current-scope)))
- (dolist (tag tags)
- (push tag (gethash (citre-get-tag-field 'name tag) groups)))
(setq visible (sort visible (lambda (a b) (my/citre-backend--rank-less-p a b context current-scope))))
(if (not (eq my/citre-completion-deduplication 'name))
visible
- (let ((seen (make-hash-table :test #'equal)) result)
+ (let ((groups (make-hash-table :test #'equal))
+ (seen (make-hash-table :test #'equal)) result)
+ (dolist (tag tags)
+ (push tag (gethash (citre-get-tag-field 'name tag) groups)))
(dolist (tag visible (nreverse result))
(let ((name (citre-get-tag-field 'name tag)))
(unless (gethash name seen)
@@ -316,16 +322,15 @@ Each function receives a receiver name and the beginning of its member expressio
(context (list revision (car bounds) major-mode citre-tags-substr-completion
(buffer-substring-no-properties (line-beginning-position) (car bounds))
member-context))
- (tick (buffer-chars-modified-tick))
(cache my/citre-backend--completion-cache)
(tags (if (and revision (equal context (plist-get cache :context))
(string-prefix-p (plist-get cache :prefix) symbol))
- (progn (plist-put cache :tick tick) (plist-get cache :tags))
+ (plist-get cache :tags)
(let ((tags (my/citre-backend--prepare-completions
(my/citre-backend--query 'completion) member-context)))
(setq my/citre-backend--completion-cache
(list :context context :prefix (substring-no-properties symbol)
- :tick tick :tags tags))
+ :tags tags))
tags))))
(when tags (list (car bounds) (cdr bounds) tags)))))
@@ -359,6 +364,44 @@ Each function receives a receiver name and the beginning of its member expressio
"Return the current file's symbols for Imenu, including unsaved changes."
(my/citre-backend--query 'imenu))
+(defun my/citre-backend--reference-root ()
+ "Return the directory searched for references."
+ (or (and my/citre-index--state (my/citre-index-state-root my/citre-index--state))
+ (when-let* ((project (project-current nil))) (project-root project))
+ default-directory))
+
+(defun my/citre-backend-references-of-id (identifier)
+ "Return textual references to IDENTIFIER below the project root.
+Universal Ctags records definitions only, so references come from a
+whole-word search that honours the project's ignore files."
+ (when-let* ((program (executable-find my/citre-reference-program))
+ (name (substring-no-properties identifier))
+ (root (my/citre-backend--reference-root)))
+ (unless (string-empty-p name)
+ (let ((default-directory root) tags)
+ (with-temp-buffer
+ (process-file program nil t nil
+ "--no-heading" "--line-number" "--color=never"
+ "--word-regexp" "--fixed-strings" "--" name ".")
+ (goto-char (point-min))
+ (while (not (eobp))
+ ;; Citre renders the matched line from the file itself, so only the
+ ;; location is recorded here.
+ (when (looking-at "\\([^:\n]+\\):\\([0-9]+\\):")
+ (push (citre-make-tag
+ 'name name
+ 'ext-abspath (expand-file-name (match-string-no-properties 1) root)
+ 'line (match-string-no-properties 2)
+ 'extras "reference")
+ tags))
+ (forward-line 1)))
+ (nreverse tags)))))
+
+(defun my/citre-backend-references ()
+ "Return textual references to the symbol at point."
+ (when-let* ((symbol (citre-tags-symbol-at-point)))
+ (my/citre-backend-references-of-id symbol)))
+
(defun my/citre-backend-usable-p ()
"Whether the current buffer has an automatic or conventional tags index."
(or my/citre-index--state (citre-tags-usable-p)))
@@ -381,6 +424,8 @@ Each function receives a receiver name and the beginning of its member expressio
:completions-fn #'my/citre-backend-completions
:defs-fn #'my/citre-backend-definitions
:defs-of-id-fn #'my/citre-backend-definitions-of-id
+ :refs-fn #'my/citre-backend-references
+ :refs-of-id-fn #'my/citre-backend-references-of-id
:id-list-fn #'my/citre-backend-identifiers
:tags-in-buffer-fn #'my/citre-backend-imenu
:after-jump-fn #'my/citre-index-attach))