Move all *.el files to ./lisp

This commit is contained in:
Jens Luedicke
2025-09-09 16:08:15 +02:00
parent 8567e48c4c
commit a4328ce3ba
19 changed files with 894 additions and 39 deletions

343
lisp/symbol-finder.el Normal file
View File

@@ -0,0 +1,343 @@
;;; symbol-finder.el --- Jump to symbol definitions using Python symbol finder -*- lexical-binding: t; -*-
;;; Commentary:
;; This package provides integration with the Python symbol_finder.py tool
;; for quickly jumping to symbol definitions in C++ and QML files.
;;
;; Usage:
;; 1. Add this file to your Emacs load path
;; 2. Add (require 'symbol-finder) to your init.el
;; 3. Optionally customize symbol-finder-python-script path
;; 4. Use M-. to jump to definition (or customize keybinding)
;;; Code:
(defgroup symbol-finder nil
"Jump to symbol definitions using Python symbol finder."
:group 'tools)
(defcustom symbol-finder-python-script
(expand-file-name "symbol_finder.py"
(file-name-directory (or load-file-name buffer-file-name)))
"Path to the symbol_finder.py script."
:type 'file
:group 'symbol-finder)
(defcustom symbol-finder-root-directory nil
"Root directory for symbol indexing. If nil, use project root or default-directory."
:type '(choice (const :tag "Auto-detect" nil)
(directory :tag "Directory"))
:group 'symbol-finder)
(defcustom symbol-finder-cache-directory ".symbol_cache"
"Directory name for symbol cache (relative to root or absolute path)."
:type 'string
:group 'symbol-finder)
(defcustom symbol-finder-use-absolute-cache nil
"If non-nil, treat cache-directory as an absolute path."
:type 'boolean
:group 'symbol-finder)
(defcustom symbol-finder-auto-index t
"Whether to automatically index files when cache is missing."
:type 'boolean
:group 'symbol-finder)
(defvar symbol-finder--history nil
"History of searched symbols.")
(defvar symbol-finder--marker-ring (make-ring 20)
"Ring of markers for jumping back.")
(defun symbol-finder--get-root ()
"Get the root directory for symbol operations."
(or symbol-finder-root-directory
(when (fboundp 'projectile-project-root)
(projectile-project-root))
(when (fboundp 'project-root)
(car (project-roots (project-current))))
default-directory))
(defun symbol-finder--get-cache-dir ()
"Get the cache directory path."
(if (or symbol-finder-use-absolute-cache
(file-name-absolute-p symbol-finder-cache-directory))
symbol-finder-cache-directory
(expand-file-name symbol-finder-cache-directory (symbol-finder--get-root))))
(defun symbol-finder--run-command (args)
"Run symbol_finder.py with ARGS and return output."
(let* ((root-dir (symbol-finder--get-root))
(cache-dir (symbol-finder--get-cache-dir))
(default-directory root-dir)
(cmd (format "python3 %s --root %s --cache-dir %s %s"
(shell-quote-argument symbol-finder-python-script)
(shell-quote-argument root-dir)
(shell-quote-argument cache-dir)
args)))
(shell-command-to-string cmd)))
(defun symbol-finder-index (&optional force)
"Index all source files. With prefix arg FORCE, force reindex."
(interactive "P")
(message "Indexing files...")
(let* ((args (if force "--index --force" "--index"))
(output (symbol-finder--run-command args)))
(message "%s" (string-trim output))))
(defun symbol-finder--parse-emacs-output (output)
"Parse Emacs-formatted output from symbol finder."
(let ((lines (split-string output "\n" t))
results)
(dolist (line lines)
(when (string-match "\\(.+?\\):\\([0-9]+\\):\\([0-9]+\\):\\(.*\\)" line)
(push `(:file ,(match-string 1 line)
:line ,(string-to-number (match-string 2 line))
:column ,(string-to-number (match-string 3 line))
:context ,(match-string 4 line))
results)))
(nreverse results)))
(defun symbol-finder--push-mark ()
"Push current position to marker ring."
(ring-insert symbol-finder--marker-ring (point-marker)))
(defun symbol-finder-jump-to-definition (&optional symbol)
"Jump to definition of SYMBOL at point or prompt for symbol."
(interactive)
(message "symbol-finder-jump-to-definition called") ;; Debug
(let* ((symbol (or symbol
(thing-at-point 'symbol t)
(read-string "Symbol: " nil 'symbol-finder--history)))
(cmd (format "--definition %s --emacs" (shell-quote-argument symbol)))
(output (symbol-finder--run-command cmd)))
(message "Command: python3 %s %s" symbol-finder-python-script cmd) ;; Debug
(message "Output: %s" output) ;; Debug
(let ((results (symbol-finder--parse-emacs-output output)))
(cond
((null results)
(message "No definition found for '%s'" symbol))
((= 1 (length results))
(let ((result (car results)))
(symbol-finder--push-mark)
(find-file (plist-get result :file))
(goto-char (point-min))
(forward-line (1- (plist-get result :line)))
(move-to-column (1- (plist-get result :column)))
(pulse-momentary-highlight-one-line (point))))
(t
(symbol-finder--select-and-jump results symbol))))))
(defun symbol-finder-find-references (&optional symbol)
"Find all references to SYMBOL at point or prompt for symbol."
(interactive)
(let* ((symbol (or symbol
(thing-at-point 'symbol t)
(read-string "Find references to: " nil 'symbol-finder--history)))
(output (symbol-finder--run-command
(format "--references %s --emacs" (shell-quote-argument symbol)))))
(with-current-buffer (get-buffer-create "*Symbol References*")
(let ((inhibit-read-only t))
(erase-buffer)
(insert output)
(grep-mode)
(goto-char (point-min)))
(display-buffer (current-buffer)))))
(defun symbol-finder-find-symbol (&optional exact)
"Find symbol by name. With prefix arg EXACT, use exact match."
(interactive "P")
(let* ((initial (thing-at-point 'symbol t))
(symbol (read-string (format "Find symbol%s: " (if exact " (exact)" ""))
initial 'symbol-finder--history))
(args (format "--find %s --emacs %s"
(shell-quote-argument symbol)
(if exact "--exact" "")))
(output (symbol-finder--run-command args))
(results (symbol-finder--parse-emacs-output output)))
(cond
((null results)
(message "No symbols found matching '%s'" symbol))
((= 1 (length results))
(let ((result (car results)))
(symbol-finder--push-mark)
(find-file (plist-get result :file))
(goto-char (point-min))
(forward-line (1- (plist-get result :line)))
(pulse-momentary-highlight-one-line (point))))
(t
(symbol-finder--select-and-jump results symbol)))))
(defun symbol-finder--select-and-jump (results symbol)
"Let user select from RESULTS and jump to selected SYMBOL."
(let* ((choices (mapcar (lambda (r)
(format "%s:%d: %s"
(file-name-nondirectory (plist-get r :file))
(plist-get r :line)
(plist-get r :context)))
results))
(choice (completing-read (format "Select %s: " symbol) choices nil t))
(index (cl-position choice choices :test 'equal)))
(when index
(let ((result (nth index results)))
(symbol-finder--push-mark)
(find-file (plist-get result :file))
(goto-char (point-min))
(forward-line (1- (plist-get result :line)))
(pulse-momentary-highlight-one-line (point))))))
(defun symbol-finder-cache-status ()
"Show cache status and statistics."
(interactive)
(let* ((root-dir (symbol-finder--get-root))
(output (symbol-finder--run-command "--stats")))
(with-current-buffer (get-buffer-create "*Symbol Cache Status*")
(let ((inhibit-read-only t))
(erase-buffer)
(insert "Symbol Finder Cache Status\n")
(insert "==========================\n\n")
(insert (format "Root directory: %s\n" root-dir))
(insert (format "Python script: %s\n\n" symbol-finder-python-script))
(insert output)
(goto-char (point-min)))
(display-buffer (current-buffer)))))
(defun symbol-finder-diagnose ()
"Diagnose symbol-finder setup and keybindings."
(interactive)
(let ((global-binding (global-key-binding (kbd "M-.")))
(local-binding (local-key-binding (kbd "M-.")))
(mode-binding (and (boundp 'symbol-finder-mode-map)
(lookup-key symbol-finder-mode-map (kbd "M-.")))))
(message "=== Symbol-Finder Diagnostic ===")
(message "Symbol-finder-mode: %s" (if symbol-finder-mode "ON" "OFF"))
(message "Python script: %s" (if (file-exists-p symbol-finder-python-script)
"FOUND" "NOT FOUND"))
(message "M-. global binding: %s" global-binding)
(message "M-. local binding: %s" local-binding)
(message "M-. mode binding: %s" mode-binding)
(message "Active minor modes: %s" (mapcar 'car minor-mode-alist))
(when (eq global-binding 'xref-find-definitions)
(message "NOTE: M-. is bound to xref. You may want to use symbol-finder-setup-override-xref"))))
(defun symbol-finder-setup-override-xref ()
"Override xref M-. binding with symbol-finder in current buffer."
(interactive)
(local-set-key (kbd "M-.") 'symbol-finder-jump-to-definition)
(local-set-key (kbd "M-,") 'symbol-finder-pop-mark)
(local-set-key (kbd "M-?") 'symbol-finder-find-references)
(message "M-. now bound to symbol-finder-jump-to-definition in this buffer"))
(defun symbol-finder-pop-mark ()
"Pop back to previous position in marker ring."
(interactive)
(if (ring-empty-p symbol-finder--marker-ring)
(message "No previous position")
(let ((marker (ring-remove symbol-finder--marker-ring 0)))
(switch-to-buffer (marker-buffer marker))
(goto-char marker))))
(defun symbol-finder-update-file ()
"Update index for current file."
(interactive)
(when buffer-file-name
(message "Updating index for %s..." buffer-file-name)
(let* ((args (format "--index --force --root %s"
(shell-quote-argument (file-name-directory buffer-file-name))))
(output (symbol-finder--run-command args)))
(message "Index updated"))))
;; Auto-update on save
(defun symbol-finder--after-save-hook ()
"Hook to update index after saving."
(when (and buffer-file-name
(string-match-p "\\.\\(cpp\\|cc\\|cxx\\|c\\+\\+\\|hpp\\|h\\|hh\\|hxx\\|h\\+\\+\\|qml\\|js\\)$"
buffer-file-name))
(symbol-finder-update-file)))
(defcustom symbol-finder-auto-update-on-save nil
"Whether to automatically update index on file save."
:type 'boolean
:group 'symbol-finder
:set (lambda (symbol value)
(set-default symbol value)
(if value
(add-hook 'after-save-hook 'symbol-finder--after-save-hook)
(remove-hook 'after-save-hook 'symbol-finder--after-save-hook))))
;; Minor mode for keybindings
(defvar symbol-finder-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "M-.") 'symbol-finder-jump-to-definition)
(define-key map (kbd "M-?") 'symbol-finder-find-references)
(define-key map (kbd "M-,") 'symbol-finder-pop-mark)
(define-key map (kbd "C-c s f") 'symbol-finder-find-symbol)
(define-key map (kbd "C-c s i") 'symbol-finder-index)
(define-key map (kbd "C-c s u") 'symbol-finder-update-file)
map)
"Keymap for symbol-finder-mode.")
;;;###autoload
(define-minor-mode symbol-finder-mode
"Minor mode for symbol navigation using Python symbol finder."
:lighter " SymF"
:keymap symbol-finder-mode-map
:group 'symbol-finder
(when symbol-finder-mode
;; Debug message
(message "Symbol-finder-mode activated. M-. bound to: %s"
(lookup-key symbol-finder-mode-map (kbd "M-.")))
(when (and symbol-finder-auto-index
(not (file-exists-p
(expand-file-name symbol-finder-cache-directory
(symbol-finder--get-root)))))
(when (y-or-n-p "No symbol cache found. Index files now?")
(symbol-finder-index)))))
;;;###autoload
(define-globalized-minor-mode global-symbol-finder-mode
symbol-finder-mode
(lambda ()
(when (and (not (minibufferp))
(string-match-p "\\.\\(cpp\\|cc\\|cxx\\|c\\+\\+\\|hpp\\|h\\|hh\\|hxx\\|h\\+\\+\\|qml\\|js\\)$"
(or buffer-file-name "")))
(symbol-finder-mode 1))))
;; Compatibility with xref (optional)
(when (fboundp 'xref-make)
(defun symbol-finder-xref-backend ()
"Symbol finder backend for xref."
'symbol-finder)
(cl-defmethod xref-backend-identifier-at-point ((_backend (eql symbol-finder)))
(thing-at-point 'symbol t))
(cl-defmethod xref-backend-definitions ((_backend (eql symbol-finder)) identifier)
(let* ((output (symbol-finder--run-command
(format "--definition %s --emacs" (shell-quote-argument identifier))))
(results (symbol-finder--parse-emacs-output output)))
(mapcar (lambda (r)
(xref-make (plist-get r :context)
(xref-make-file-location (plist-get r :file)
(plist-get r :line)
(1- (plist-get r :column)))))
results)))
(cl-defmethod xref-backend-references ((_backend (eql symbol-finder)) identifier)
(let* ((output (symbol-finder--run-command
(format "--references %s --emacs" (shell-quote-argument identifier))))
(results (symbol-finder--parse-emacs-output output)))
(mapcar (lambda (r)
(xref-make (plist-get r :context)
(xref-make-file-location (plist-get r :file)
(plist-get r :line)
(1- (plist-get r :column)))))
results)))
(add-hook 'symbol-finder-mode-hook
(lambda ()
(add-hook 'xref-backend-functions 'symbol-finder-xref-backend nil t))))
(provide 'symbol-finder)
;;; symbol-finder.el ends here