| git.druid.rocks | index | druid520 | radium | radium-tags.el |
radium-tags.el
;;; radium-tags.el, gnu global (gtags) backed symbol lookup + completion -*- lexical-binding: t; -*-
;;
;; requires gnu-global (the `gtags`/`global`/`gtags-cscope` binaries) on
;; PATH. this is the substitute for lsp's jump-to-def and symbol
;; completion: build a database once with `C-c t g`, then M-. (ggtags'
;; own dwim finder) and completion-at-point both use it.
(require 'radium-conf)
(declare-function ggtags-completion-at-point "ggtags")
;; a second, machine-wide gtags db over the system headers, so
;; completion/jump-to-def fall back to it for anything not in the
;; project's own db - e.g. printf right after #include <stdio.h>, same
;; c-mode completion-at-point radium-proto.el already wires up. built
;; once, outside this repo (it's a plain index of whatever's actually
;; installed on this machine, not something to version):
;; mkdir -p ~/.cache/gtags-sys
;; cd /usr/include && gtags ~/.cache/gtags-sys
(let ((sys-db (expand-file-name ".cache/gtags-sys" "~")))
(when (file-directory-p sys-db)
(setenv "GTAGSLIBPATH" sys-db)))
(defvar radium-gtags-autogen-running nil
"non-nil while a background gtags db build is running, so i don't
stack a second one on the first.")
(defun radium-gtags-maybe-autogen ()
"build the gnu global db in the background when the project asks for
it (gtags = yes in cfg.rp's [tags]) and there isn't one yet -
opening a file just gets me tags, no C-c t g to remember."
(when (and (string= (radium-conf 'tags 'gtags) "yes")
(executable-find "gtags")
(not radium-gtags-autogen-running))
(let ((root (radium-build-root)))
(when (and root
(not (file-exists-p (expand-file-name "GTAGS" root))))
(setq radium-gtags-autogen-running t)
(message "radium: building gtags db for %s..."
(file-name-nondirectory (directory-file-name root)))
(let ((default-directory root))
(make-process
:name "radium-gtags"
:buffer " *radium-gtags*"
:command (list "gtags")
:sentinel
(lambda (_proc event)
(setq radium-gtags-autogen-running nil)
(message (if (string-prefix-p "finished" event)
"ok: gtags db built."
(format "err: gtags failed: %s." event))))))))))
(use-package ggtags
;; scheme is deliberately not here: geiser owns M-. in scheme
;; buffers, and its module-aware definitions beat a token index.
:hook ((c-mode asm-mode emacs-lisp-mode cperl-mode sh-mode ada-mode)
. ggtags-mode)
:config
(setq ggtags-completing-read-function nil
ggtags-sort-by-nearness t
ggtags-auto-jump-to-first-match t)
(add-hook 'ggtags-mode-hook
(lambda ()
(add-hook 'completion-at-point-functions #'ggtags-completion-at-point nil t)))
(add-hook 'ggtags-mode-hook #'radium-gtags-maybe-autogen))
(use-package dumb-jump
;; dumb-jump reaches for rg/ag over grep on its own if either is on
;; PATH - pin it to grep so it doesn't silently pick one up.
:custom (dumb-jump-force-searcher 'grep)
:init (add-hook 'xref-backend-functions #'dumb-jump-xref-activate t))
(defun radium-gtags-create ()
"i build/refresh the gnu global tags database at the project root."
(interactive)
(let ((default-directory (or (project-root (project-current t)) default-directory)))
(compile "gtags")))
(require 'fileloop)
(defun radium-global-rename (old new)
"i rename every occurrence of symbol OLD to NEW across every file gnu
global says references or defines it - a safe project-wide rename
without a language server. i run it as an ordinary interactive
query-replace, confirmed file by file."
(interactive
(let ((sym (or (thing-at-point 'symbol t) (read-string "symbol: "))))
(list sym (read-string (format "rename `%s' to: " sym)))))
(unless (executable-find "global")
(user-error "err: gnu global not found on path."))
(let* ((root (or (locate-dominating-file default-directory "GTAGS") default-directory))
(default-directory root)
(files (mapcar #'expand-file-name
(split-string
(shell-command-to-string
(format "global --result=path -r %s 2>/dev/null"
(shell-quote-argument old)))
"\n" t))))
(unless files
(user-error "err: gnu global found no references to `%s' (build the db with C-c t g?)." old))
(fileloop-initialize-replace
(format "\\_<%s\\_>" (regexp-quote old)) new files 'default)
(fileloop-continue)))
(global-set-key (kbd "C-c t g") #'radium-gtags-create)
(global-set-key (kbd "C-c t r") #'radium-global-rename)
(provide 'radium-tags)