;;; dumb-jump.el --- Jump to definition for 60+ languages without configuration -*- lexical-binding: t; -*- ;; Copyright (C) 2015-2026 jack angers ;; Author: jack angers and contributors ;; Url: https://github.com/jacktasia/dumb-jump ;; Version: 0.5.5 ;; Package-Requires: ((emacs "26.1")) ;; Keywords: programming ;; Dumb Jump is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; ;; Dumb Jump is distributed in the hope that it will be useful, but WITHOUT ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public ;; License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with Dumb Jump. If not, see http://www.gnu.org/licenses. ;;; Commentary: ;; Dumb Jump is an Emacs "jump to definition" package with support for 60+ programming languages that favors ;; "just working" over speed or accuracy. This means minimal -- and ideally zero -- configuration with absolutely ;; no stored indexes (TAGS) or persistent background processes. ;; ;; Dumb Jump provides a xref-based interface for jumping to ;; definitions. It is based on tools such as grep, the silver searcher ;; (https://geoff.greer.fm/ag/), ripgrep ;; (https://github.com/BurntSushi/ripgrep) or git-grep ;; (https://git-scm.com/docs/git-grep). ;; ;; To enable Dumb Jump, add the following to your initialisation file: ;; ;; (add-hook 'xref-backend-functions #'dumb-jump-xref-activate) ;; ;; Now pressing M-. on an identifier should open a buffer at the place ;; where it is defined, or a list of candidates if uncertain. This ;; list can be navigated using M-g M-n (next-error) and M-g M-p ;; (previous-error). ;;; Code: (require 'xref) (require 'cl-generic) (require 'cl-lib) (require 'seq) (require 'subr-x) (require 'json) (require 'project nil 'noerror) (defun dumb-jump--chop-prefix (prefix text) "Remove PREFIX from TEXT when present." (if (and (stringp prefix) (stringp text) (string-prefix-p prefix text)) (substring text (length prefix)) text)) (defun dumb-jump--contains-p (needle text &optional ignore-case) "Return non-nil when TEXT contains NEEDLE as a literal substring. When IGNORE-CASE is non-nil, match without case sensitivity." (and (stringp needle) (stringp text) (save-match-data (let ((case-fold-search ignore-case)) (if (string-match-p (regexp-quote needle) text) t nil))))) (defun dumb-jump--index-of (needle text) "Return the index of NEEDLE in TEXT, or nil when not found." (when (and (stringp needle) (stringp text)) (save-match-data (string-match-p (regexp-quote needle) text)))) (defun dumb-jump--join (separator strings) "Join STRINGS using SEPARATOR." (mapconcat #'identity strings separator)) (defun dumb-jump--match (regexp text &optional start) "Return list of REGEXP matches in TEXT from START, or nil." (when (and (stringp regexp) (stringp text)) (save-match-data (when (string-match regexp text start) (let* ((group-count (/ (length (match-data t)) 2)) (i 0) matches) (while (< i group-count) (push (match-string i text) matches) (setq i (1+ i))) (nreverse matches)))))) (defun dumb-jump--matched-positions-all (regexp text &optional start) "Return a list of match ranges for REGEXP in TEXT from START. Each element is a cons cell of the form (START . END)." (when (and (stringp regexp) (stringp text)) (save-match-data (let ((pos (or start 0)) matches) (while (string-match regexp text pos) (let ((beg (match-beginning 0)) (end (match-end 0))) (push (cons beg end) matches) ;; Avoid infinite loops if REGEXP can match empty strings. (setq pos (if (= beg end) (1+ end) end)))) (nreverse matches))))) (defun dumb-jump--replace (old new text) "Replace all OLD literal matches in TEXT with NEW." (replace-regexp-in-string (regexp-quote old) (or new "") (or text "") t t)) (defun dumb-jump--split (regexp text &optional omit-nulls) "Split TEXT on REGEXP and optionally OMIT-NULLS." (split-string (or text "") regexp omit-nulls)) (defun dumb-jump--trim (text) "Trim leading and trailing whitespace from TEXT." (string-trim (or text ""))) (defun dumb-jump--select-choice (choices &optional prompt) "Choose one of CHOICES with `completing-read'. PROMPT defaults to \"Jump to: \"." (completing-read (or prompt "Jump to: ") choices nil t)) (defun dumb-jump--show-preview (text) "Display TEXT preview to the user." (message "%s" text)) (defun dumb-jump--find-index (pred list) "Return index of first element in LIST satisfying PRED, or nil." (let ((i 0)) (catch 'found (dolist (elem list) (when (funcall pred elem) (throw 'found i)) (setq i (1+ i))) nil))) (defun dumb-jump--zip-pair (list1 list2) "Return list of cons pairs from LIST1 and LIST2." (cl-mapcar #'cons list1 list2)) (defun dumb-jump--splice (pred rep list) "Replace elements in LIST matching PRED with result of calling REP on them. REP can return a list to splice in multiple elements." (apply #'append (mapcar (lambda (it) (if (funcall pred it) (funcall rep it) (list it))) list))) (defgroup dumb-jump nil "Easily jump to project function and variable definitions." :group 'tools :group 'convenience) ;;;###autoload (defvar dumb-jump-mode-map (let ((map (make-sparse-keymap))) (define-key map (kbd "C-M-g") 'dumb-jump-go) (define-key map (kbd "C-M-p") 'dumb-jump-back) (define-key map (kbd "C-M-q") 'dumb-jump-quick-look) map)) (defvar dumb-jump--search-mode 'definitions "Current search mode: `definitions' (default) or `references'.") (defcustom dumb-jump-window 'current "Which window to use when jumping. Valid options are \\='current (default) or \\='other." :group 'dumb-jump :type '(choice (const :tag "Current window" current) (const :tag "Other window" other))) (defcustom dumb-jump-use-visible-window t "If true will jump in a visible window that shows the opened file." :group 'dumb-jump :type 'boolean) (defcustom dumb-jump-selector 'completing-read "Which selector to use when there is multiple choices. The available selectors are: - helm : https://emacs-helm.github.io/helm/ - ivy : https://github.com/abo-abo/swiper - Completing Read: Emacs default `completing-read'. When selecting helm or ivy, if the corresponding package is not installed, the selector falls back to `completing-read'." :group 'dumb-jump :type '(choice (const :tag "Helm" helm) (const :tag "Ivy" ivy) (const :tag "Completing Read" completing-read))) (defcustom dumb-jump-ivy-jump-to-selected-function #'dumb-jump-ivy-jump-to-selected "Prompts user for a choice using ivy then dumb-jump to that choice." :group 'dumb-jump :type 'function) (defcustom dumb-jump-prefer-searcher nil "The preferred searcher command line tool to use. The available choices are: - \\='ag : https://github.com/ggreer/the_silver_searcher - \\='rg : https://github.com/BurntSushi/ripgrep - \\='grep : https://en.wikipedia.org/wiki/Grep - \\='gnu-grep : https://www.gnu.org/software/grep/manual/grep.html - \\='git-grep : https://git-scm.com/docs/git-grep - \\='git-grep-plus-ag If nil, select the most optimal searcher at runtime, looking for all tools listed above except for git-grep in the order of that list. However, the choice is overridden by `dumb-jump-force-searcher' selection unless that is nil." :group 'dumb-jump :type '(choice (const :tag "Best Available" nil) (const :tag "ag" ag) (const :tag "rg" rg) (const :tag "grep" grep) (const :tag "gnu-grep" gnu-grep) (const :tag "git grep" git-grep) (const :tag "git grep + ag" git-grep-plus-ag))) (defcustom dumb-jump-force-searcher nil "Search tool override of `dumb-jump-prefer-searcher' selection. By default this is nil to honour the choice made by `dumb-jump-prefer-searcher'. However, you may want to override that choice for some projects by identifying one of two preferred choice overriding methods: - Override preferred via user-specified function: Enter the name of a function that takes the current directory and return the search tool symbol to use for this project or nil if the choice made in `dumb-jump-prefer-searcher' must be honoured. - Override preferred for directories needing git-grep: Specify one or more project directories where the git-grep search tool must be used. - Override to a specific tool. That is mostly useful to set the overriding inside the .dir-locals.el of the directory. The choices are: \\='ag, \\='rg, \\='grep, \\='gnu-grep, \\='git-grep, or \\='git-grep-plus-ag." :group 'dumb-jump :type '(choice (const :tag "nil: honour `dumb-jump-prefer-searcher' choice." nil) (function :tag "Override preferred via user-specified function") (repeat :tag "Override preferred for directories needing git-grep" (directory :tag "Repo root")) (const :tag "ag" ag) (const :tag "rg" rg) (const :tag "grep" grep) (const :tag "gnu grep" gnu-grep) (const :tag "git grep" git-grep) (const :tag "git grep + ag" git-grep-plus-ag))) (defcustom dumb-jump-grep-prefix "LANG=C" "Prefix to grep command. Seemingly makes it faster for pure text." :group 'dumb-jump :type 'string) (defcustom dumb-jump-grep-cmd "grep" "The path to grep. By default assumes it is in path." :group 'dumb-jump :type 'string) (defcustom dumb-jump-ag-cmd "ag" "The path to the silver searcher. By default assumes it is in path." :group 'dumb-jump :type 'string) (defcustom dumb-jump-rg-cmd "rg" "The path to ripgrep. By default assumes it is in path." :group 'dumb-jump :type 'string) (defcustom dumb-jump-git-grep-cmd "git grep" "The path to git grep. By default assumes it is in path." :group 'dumb-jump :type 'string) ;; --------------------------------------------------------------------------- ;; Dumb Jump Regular Expressions (see `dumb-jump-find-rules' docstring) (defcustom dumb-jump-force-using-space-bracket-exp (eq system-type 'windows-nt) "When t the `\\\\s` in regexp code strings is replaced by `[[:space:]]`. Otherwise it is left in the regular expression. Defaults to t under Windows because older gnu grep versions have problem dealing with `\\\\s` to match end of words due to the 2 bytes used to identify the end of line." :group 'dumb-jump :type 'boolean) (defun dumb-jump-use-space-bracket-exp-for (variant) "Return t when `\\\\s` must be replaced by `[[:space]]` for VARIANT. VARIANT must be a one of the following symbols: ag, rg, grep, gnu-grep, git-grep or git-grep-plus-ag." (and dumb-jump-force-using-space-bracket-exp (memq variant '(grep gnu-grep git-grep git-grep-plus-ag)))) ;; word-boundary regexps: they replace the "\\j" in dumb-jump regexes. ;; [:todo 2026-01-29, by Pierre Rouleau: Should we not exclude '_' from word boundary?] (defcustom dumb-jump-ag-word-boundary "(?![a-zA-Z0-9\\?\\*-])" "Regexp that replaces `\\j` in dumb-jump regexes for ag search. `\\b` thinks `-` is a word boundary. When this matters use `\\j` instead and ag will use this value." :group 'dumb-jump :type 'string) ;; [:todo 2026-01-29, by Pierre Rouleau: Should we not exclude '_' from word boundary?] (defcustom dumb-jump-rg-word-boundary "($|[^a-zA-Z0-9\\?\\*-])" "Regexp that replaces `\\j` in dumb-jump regexes for rg search. `\\b` thinks `-` is a word boundary. When this matters use `\\j` instead and rg will use this value." :group 'dumb-jump :type 'string) ;; [:todo 2026-01-29, by Pierre Rouleau: Should we not exclude '_' from word boundary?] (defcustom dumb-jump-git-grep-word-boundary "($|[^a-zA-Z0-9\\?\\*-])" "Regexp that replaces `\\j` in dumb-jump regexes for git-grep search. `\\b` thinks `-` is a word boundary. When this matters use `\\j` instead and git grep will use this value." :group 'dumb-jump :type 'string) ;; [:todo 2026-01-29, by Pierre Rouleau: Should we not exclude '_' from word boundary?] (defcustom dumb-jump-grep-word-boundary "($|[^a-zA-Z0-9\\?\\*-])" "Regexp that replaces `\\j` in dumb-jump regexes for grep search. `\\b` thinks `-` is a word boundary. When this matters use `\\j` instead and grep will use this value." :group 'dumb-jump :type 'string) ;; [:todo 2026-01-29, by Pierre Rouleau: Should we not exclude '_' from word boundary?] (defcustom dumb-jump-gnu-grep-word-boundary "($|[^a-zA-Z0-9\\?\\*-])" "Regexp that replaces `\\j` in dumb-jump regexes for gnu grep search. `\\b` thinks `-` is a word boundary. When this matters use `\\j` instead and grep will use this value." :group 'dumb-jump :type 'string) ;; -- (defcustom dumb-jump-fallback-regex "\\bJJJ\\j" "When `dumb-jump-fallback-search' is t use this regex. Defaults to boundary search of symbol under point." :group 'dumb-jump :type 'string) (defcustom dumb-jump-fallback-search t "If nothing is found with normal search fallback searching fallback regex." :group 'dumb-jump :type 'boolean) (defcustom dumb-jump-zgrep-cmd "zgrep" "The path to grep to use for gzipped files. By default assumes it is in path." :group 'dumb-jump :type 'string) (defcustom dumb-jump-grep-args "-REn" "Grep command args [R]ecursive, [E]xtended regexes, and show line [n]umbers." :group 'dumb-jump :type 'string) (defcustom dumb-jump-gnu-grep-args "-rEn" "Grep command args [r]ecursive and [E]xtended regexes, and show line [n]umbers." :group 'dumb-jump :type 'string) (defcustom dumb-jump-max-find-time 2 "Number of seconds a grep/find command can take before issuing a warning. The warning recommend using a faster search tool and config." :group 'dumb-jump :type 'integer) (defcustom dumb-jump-functions-only nil "Should we only jump to functions?" :group 'dumb-jump :type 'boolean) (defcustom dumb-jump-quiet nil "If non-nil Dumb Jump will not log anything to *Messages*. Note that it prevents printing when `dumb-jump-debug' is non-nil." :group 'dumb-jump :type 'boolean) (defcustom dumb-jump-ignore-context nil "If non-nil Dumb Jump will ignore the context of point when jumping." :group 'dumb-jump :type 'boolean) (defcustom dumb-jump-git-grep-search-untracked t "If non-nil Dumb Jump will also search untracked files when using git-grep." :group 'dumb-jump :type 'boolean) (defcustom dumb-jump-git-grep-search-args "" "Appends the passed arguments to the git-grep search function. Default: \"\"." :group 'dumb-jump :type 'string) (defcustom dumb-jump-ag-search-args "" "Appends the passed arguments to the ag search function. Default: \"\"." :group 'dumb-jump :type 'string) (defcustom dumb-jump-rg-search-args "--pcre2" "Appends the passed arguments to the rg search function. Default: \"--pcre2\"." :group 'dumb-jump :type 'string) (defcustom dumb-jump-search-type-org-only-org t "If non nil restrict type ag/rg to org file. If nil add also the language type of current src block." :group 'dumb-jump :type 'boolean) ;; [:todo 2026-01-29, by Pierre Rouleau: Why 3 backslashes in front of '('? ] (defcustom dumb-jump-find-rules ;;-- elisp '((:language "elisp" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\((defun|cl-defun|cl-defgeneric|cl-defmethod|cl-defsubst)\\s+JJJ\\j" :tests ("(defun test (blah)" "(defun test\n" "(cl-defun test (blah)" "(cl-defun test\n") :not ("(defun test-asdf (blah)" "(defun test-blah\n" "(cl-defun test-asdf (blah)" "(cl-defun test-blah\n" "(defun tester (blah)" "(defun test? (blah)" "(defun test- (blah)")) (:language "elisp" :type "function" ; macros :supports ("ag" "grep" "rg" "git-grep") :regex "\\((defmacro|cl-defmacro|cl-define-compiler-macro)\\s+JJJ\\j" :tests ("(defmacro test (blah)" "(defmacro test\n") :not ("(defmacro test-asdf (blah)" "(defmacro test-blah\n" "(defmacro tester (blah)" "(defmacro test? (blah)" "(defmacro test- (blah)")) (:language "elisp" :type "function" ; hydras :supports ("ag" "grep" "rg" "git-grep") :regex "\\(defhydra\\b\\s*JJJ\\j" :tests ("(defhydra test " "(defhydra test\n" "(defhydra test (blah\n" "(defhydra test (blah)") :not ("(defhydra tester" "(defhydra test?" "(defhydra test-" "(defhydra test? (blah\n" "(defhydra test? (blah)")) (:language "elisp" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\(defvar(-local)?\\b\\s*JJJ\\j" :tests ("(defvar test " "(defvar test\n" "(defvar-local test" "(defvar-local test\n") :not ("(defvar tester" "(defvar test?" "(defvar test-" "(defvar-local tester" "(defvar-local test?" "(defvar-local test-")) (:language "elisp" :type "variable" :supports ("ag" "grep" "rg" "ugrep" "git-grep") :regex "\\(defconst\\b\\s*JJJ\\j" :tests ("(defconst test " "(defconst test\n") :not ("(defconst tester" "(defconst test?" "(defconst test-")) (:language "elisp" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\(defcustom\\b\\s*JJJ\\j" :tests ("(defcustom test " "(defcustom test\n") :not ("(defcustom tester" "(defcustom test?" "(defcustom test-")) (:language "elisp" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\(setq\\b\\s*JJJ\\j" :tests ("(setq test 123)") :not ("setq test-blah 123)" "(setq tester" "(setq test?" "(setq test-")) (:language "elisp" :type "variable" ; let bound variables :supports ("ag" "grep" "rg" "git-grep") :regex "\\(JJJ\\s+" :skip-ref-filter t :tests ("(let ((test 123)))" "(let* ((test 123)))") :not ("(let ((test-2 123)))" "(let* ((test-2 123)))")) (:language "elisp" :type "type" ; cl-lib structured type definitions :supports ("ag" "grep" "rg" "git-grep") :regex "\\((cl-defstruct|cl-deftype)\\s+JJJ\\j" :tests ("(cl-defstruct test " "(cl-defstruct test\n" "(cl-deftype test " "(cl-deftype test\n") :not ("(cl-defstruct test-asdf (blah)" "(cl-defstruct test-blah\n" "(cl-deftype test-asdf (blah)" "(cl-deftype test-blah\n")) (:language "elisp" :type "variable" ;; variable in method signature :supports ("ag" "grep" "rg" "git-grep") :regex "\\((defun|cl-defun|cl-defgeneric|cl-defmethod)\\s*.+\\(?\\s*JJJ\\j\\s*\\)?" :tests ("(defun blah (test)" "(defun blah (test blah)" "(defun (blah test)") :not ("(defun blah (test-1)" "(defun blah (test-2 blah)" "(defun (blah test-3)")) ;;-- common lisp (:language "commonlisp" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(def(un|macro|generic|method|setf)\\s+JJJ\\j" ;; \\j usage see `dumb-jump-ag-word-boundary` :tests ("(defun test (blah)" "(defun test\n" "(defmacro test (blah)" "(defmacro test\n" "(defgeneric test (blah)" "(defgeneric test\n" "(defmethod test (blah)" "(defmethod test\n" "(defsetf test (blah)" "(defsetf test\n") :not ("(defun test-asdf (blah)" "(defun test-blah\n" "(defun tester (blah)" "(defun test? (blah)" "(defun test- (blah)" "(defmacro test-asdf (blah)" "(defmacro test-blah\n" "(defmacro tester (blah)" "(defmacro test? (blah)" "(defmacro test- (blah)" "(defgeneric test-asdf (blah)" "(defgeneric test-blah\n" "(defgeneric tester (blah)" "(defgeneric test? (blah)" "(defun test- (blah)" "(defmethod test-asdf (blah)" "(defmethod test-blah\n" "(defmethod tester (blah)" "(defmethod test? (blah)" "(defun test- (blah)" "(defsetf test-asdf (blah)" "(defsetf test-blah\n" "(defsetf tester (blah)" "(defsetf test? (blah)" "(defun test- (blah)")) (:language "commonlisp" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(define-(modify-macro|compiler-macro|setf-expander)\\s+JJJ\\j" ;; \\j usage see `dumb-jump-ag-word-boundary` :tests ("(define-modify-macro test (blah)" "(define-modify-macro test\n" "(define-compiler-macro test (blah)" "(define-compiler-macro test\n") :not ("(define-modify-macro test-asdf (blah)" "(define-modify-macro test-blah\n" "(define-modify-macro tester (blah)" "(define-modify-macro test? (blah)" "(define-modify-macro test- (blah)" "(define-compiler-macro test-asdf (blah)" "(define-compiler-macro test-blah\n" "(define-compiler-macro tester (blah)" "(define-compiler-macro test? (blah)" "(define-compiler-macro test- (blah)")) (:language "commonlisp" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(def(var|parameter|constant)\\b\\s*JJJ\\j" :tests ("(defvar test " "(defvar test\n" "(defparameter test " "(defparameter test\n" "(defconstant test " "(defconstant test\n") :not ("(defvar tester" "(defvar test?" "(defvar test-" "(defparameter tester" "(defparameter test?" "(defparameter test-" "(defconstant tester" "(defconstant test?" "(defconstant test-")) (:language "commonlisp" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(define-symbol-macro\\b\\s*JJJ\\j" :tests ("(define-symbol-macro test " "(define-symbol-macro test\n") :not ("(define-symbol-macro tester" "(define-symbol-macro test?" "(define-symbol-macro test-")) (:language "commonlisp" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(def(class|struct|type)\\b\\s*JJJ\\j" :tests ("(defclass test " "(defclass test\n" "(defstruct test " "(defstruct test\n" "(deftype test " "(deftype test\n") :not ("(defclass tester" "(defclass test?" "(defclass test-" "(defstruct tester" "(defstruct test?" "(defstruct test-" "(deftype tester" "(deftype test?" "(deftype test-")) (:language "commonlisp" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(define-condition\\b\\s*JJJ\\j" :tests ("(define-condition test " "(define-condition test\n") :not ("(define-condition tester" "(define-condition test?" "(define-condition test-")) ;;-- racket (:language "racket" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(define\\s+\\(\\s*JJJ\\j" :tests ("(define (test blah)" "(define (test\n") :not ("(define test blah" "(define (test-asdf blah)" "(define test (lambda (blah")) (:language "racket" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(define\\s+JJJ\\s*\\\(\\s*lambda" :tests ("(define test (lambda (blah" "(define test (lambda\n") :not ("(define test blah" "(define test-asdf (lambda (blah)" "(define (test)" "(define (test blah) (lambda (foo")) (:language "racket" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(let\\s+JJJ\\s*(\\\(|\\\[)*" :tests ("(let test ((blah foo) (bar bas))" "(let test\n" "(let test [(foo") :not ("(let ((test blah")) (:language "racket" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(define\\s+JJJ\\j" :tests ("(define test " "(define test\n") :not ("(define (test")) (:language "racket" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "(\\\(|\\\[)\\s*JJJ\\s+" :tests ("(let ((test 'foo" "(let [(test 'foo" "(let [(test 'foo" "(let [[test 'foo" "(let ((blah 'foo) (test 'bar)") :not ("{test foo")) (:language "racket" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(lambda\\s+\\\(?[^\(\)]*\\s*JJJ\\j\\s*\\\)?" :tests ("(lambda (test)" "(lambda (foo test)" "(lambda test (foo)") :not ("(lambda () test")) (:language "racket" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(define\\s+\\\([^\(\)]+\\s*JJJ\\j\\s*\\\)?" :tests ("(define (foo test)" "(define (foo test bar)") :not ("(define foo test" "(define (test foo" "(define (test)")) (:language "racket" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\(struct\\s+JJJ\\j" :tests ("(struct test (a b)")) ;;-- scheme (:language "scheme" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(define\\s+\\(\\s*JJJ\\j" :tests ("(define (test blah)" "(define (test\n") :not ("(define test blah" "(define (test-asdf blah)" "(define test (lambda (blah")) (:language "scheme" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(define\\s+JJJ\\s*\\\(\\s*lambda" :tests ("(define test (lambda (blah" "(define test (lambda\n") :not ("(define test blah" "(define test-asdf (lambda (blah)" "(define (test)" "(define (test blah) (lambda (foo")) (:language "scheme" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(let\\s+JJJ\\s*(\\\(|\\\[)*" :tests ("(let test ((blah foo) (bar bas))" "(let test\n" "(let test [(foo") :not ("(let ((test blah")) (:language "scheme" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(define\\s+JJJ\\j" :tests ("(define test " "(define test\n") :not ("(define (test")) (:language "scheme" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "(\\\(|\\\[)\\s*JJJ\\s+" :tests ("(let ((test 'foo" "(let [(test 'foo" "(let [(test 'foo" "(let [[test 'foo" "(let ((blah 'foo) (test 'bar)") :not ("{test foo")) (:language "scheme" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(lambda\\s+\\\(?[^\(\)]*\\s*JJJ\\j\\s*\\\)?" :tests ("(lambda (test)" "(lambda (foo test)" "(lambda test (foo)") :not ("(lambda () test")) (:language "scheme" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\(define\\s+\\\([^\(\)]+\\s*JJJ\\j\\s*\\\)?" :tests ("(define (foo test)" "(define (foo test bar)") :not ("(define foo test" "(define (test foo" "(define (test)")) ;;-- janet (:language "janet" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\(\(de\)?f\\s+JJJ\\j" :tests ("(def test (foo)")) (:language "janet" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\(var\\s+JJJ\\j" :tests ("(var test (foo)")) (:language "janet" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\(\(de\)fn-?\\s+JJJ\\j" :tests ("(defn test [foo]" "(defn- test [foo]") :not ("(defn test? [foo]" "(defn- test? [foo]")) (:language "janet" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\(defmacro\\s+JJJ\\j" :tests ("(defmacro test [foo]")) ;;-- c++ (:language "c++" :type "function" :supports ("ag" "rg" "git-grep") :regex "\\bJJJ(\\s|\\))*\\((\\w|[,&*.<>:]|\\s)*(\\))\\s*(const|->|\\{|$)|typedef\\s+(\\w|[(*]|\\s)+JJJ(\\)|\\s)*\\(" :tests ("int test(){" "my_struct (*test)(int a, int b){" "auto MyClass::test ( Builder::Builder& reference, ) -> decltype( builder.func() ) {" "int test( int *random_argument) const {" "test::test() {" "typedef int (*test)(int);") :not ("return test();)" "int test(a, b);" "if( test() ) {" "else test();")) ;; (:language "c++" :type "variable" ;; :supports ("grep") ;; :regex "(\\b\\w+|[,>])([*&]|\\s)+JJJ\\s*(\\[([0-9]|\\s)*\\])*\\s*([=,){;]|:\\s*[0-9])|#define\\s+JJJ\\b" ;; :tests ("int test=2;" "char *test;" "int x = 1, test = 2" "int test[20];" "#define test" "unsigned int test:2;")) (:language "c++" :type "variable" :supports ("ag" "rg") :regex "\\b(?!(class\\b|struct\\b|return\\b|else\\b|delete\\b))(\\w+|[,>])([*&]|\\s)+JJJ\\s*(\\[(\\d|\\s)*\\])*\\s*([=,(){;]|:\\s*\\d)|#define\\s+JJJ\\b" :tests ("int test=2;" "char *test;" "int x = 1, test = 2" "int test[20];" "#define test" "typedef int test;" "unsigned int test:2") :not ("return test;" "#define NOT test" "else test=2;")) (:language "c++" :type "type" :supports ("ag" "rg" "git-grep") :regex "\\b(class|struct|enum|union)\\b\\s*JJJ\\b\\s*(final\\s*)?(:((\\s*\\w+\\s*::)*\\s*\\w*\\s*?\\s*,*)+)?((\\{|$))|}\\s*JJJ\\b\\s*;" :tests ("typedef struct test {" "enum test {" "} test;" "union test {" "class test final: public Parent1, private Parent2{" "class test : public std::vector {") :not("union test var;" "struct test function() {")) ;;-- clojure (:language "clojure" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\(def.*\ JJJ\\j" :tests ("(def test (foo)" "(defn test [foo]" "(defn ^:some-data test [foo]" "(defn- test [foo]" "(defmacro test [foo]" "(deftask test [foo]" "(deftype test [foo]" "(defmulti test fn" "(defmethod test type" "(definterface test (foo)" "(defprotocol test (foo)" "(defrecord test [foo]" "(deftest test")) ;;-- coffeescript (:language "coffeescript" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*JJJ\\s*[=:].*[-=]>" :tests ("test = () =>" "test= =>" "test = ->" "test=()->" "test : () =>" "test: =>" "test : ->" "test:()->") :not ("# test = =>" "test = 1")) (:language "coffeescript" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*JJJ\\s*[:=][^:=-][^>]+$" :tests ("test = $" "test : [" "test = {" "test = a") :not ("test::a" "test: =>" "test == 1" "# test = 1")) (:language "coffeescript" :type "class" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*\\bclass\\s+JJJ" :tests ("class test" "class test extends") :not ("# class")) ;;-- objc : Objective-C (:language "objc" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\)\\s*JJJ(:|\\b|\\s)" :tests ("- (void)test" "- (void)test:(UIAlertView *)alertView") :not ("- (void)testnot" "- (void)testnot:(UIAlertView *)alertView")) (:language "objc" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\b\\*?JJJ\\s*=[^=\\n]+" :tests ("NSString *test = @\"asdf\"") :not ("NSString *testnot = @\"asdf\"" "NSString *nottest = @\"asdf\"")) (:language "objc" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "(@interface|@protocol|@implementation)\\b\\s*JJJ\\b\\s*" :tests ("@interface test: UIWindow") :not ("@interface testnon: UIWindow")) (:language "objc" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "typedef\\b\\s+(NS_OPTIONS|NS_ENUM)\\b\\([^,]+?,\\s*JJJ\\b\\s*" :tests ("typedef NS_ENUM(NSUInteger, test)") :not ("typedef NS_ENUMD(NSUInteger, test)")) ;;-- swift (:language "swift" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "(let|var)\\s*JJJ\\s*(=|:)[^=:\\n]+" :tests ("let test = 1234" "var test = 1234" "private lazy var test: UITapGestureRecognizer") :not ("if test == 1234:")) (:language "swift" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "func\\s+JJJ\\b\\s*(<[^>]*>)?\\s*\\(" :tests ("func test(asdf)" "func test()" "func test()") :not ("func testnot(asdf)" "func testnot()")) (:language "swift" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "(class|struct|protocol|enum)\\s+JJJ\\b\\s*?" :tests ("struct test" "struct test: Codable" "struct test" "class test:" "class test: UIWindow" "class test") :not ("class testnot:" "class testnot(object):" "struct testnot(object)")) (:language "swift" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "(typealias)\\s+JJJ\\b\\s*?=" :tests ("typealias test =") :not ("typealias testnot")) ;;-- csharp : C# (:language "csharp" :type "function" :supports ("ag" "rg") :regex "^\\s*(?:[\\w\\[\\]]+\\s+){1,3}JJJ\\s*\\\(" :tests ("int test()" "int test(param)" "static int test()" "static int test(param)" "public static MyType test()" "private virtual SomeType test(param)" "static int test()") :not ("test()" "testnot()" "blah = new test()")) (:language "csharp" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*\\bJJJ\\s*=[^=\\n)]+" :tests ("int test = 1234") :not ("if test == 1234:" "int nottest = 44")) (:language "csharp" :type "variable" :supports ("ag" "rg") :regex "\\bforeach\\s*\\(\\s*[\\w\\[\\]<>,\\.?]+\\s+JJJ\\s+in\\b" :tests ("foreach (int test in items)" "foreach (var test in items)" "foreach (string test in items)" "foreach (List test in items)") :not ("foreach (int nottest in items)" "test in items")) (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "csharp" :regex "(class|interface)\\s*JJJ\\b" :tests ("class test:" "public class test : IReadableChannel, I") :not ("class testnot:" "public class testnot : IReadableChannel, I")) ;;-- java (literally the same regexes as c#, but different tests) (:language "java" :type "function" :supports ("ag" "rg") :regex "^\\s*(?:[\\w\\[\\]]+\\s+){1,3}JJJ\\s*\\\(" :tests ("int test()" "int test(param)" "static int test()" "static int test(param)" "public static MyType test()" "private virtual SomeType test(param)" "static int test()" "private foo[] test()") :not ("test()" "testnot()" "blah = new test()" "foo bar = test()")) (:language "java" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*\\bJJJ\\s*=[^=\\n)]+" :tests ("int test = 1234") :not ("if test == 1234:" "int nottest = 44")) (:language "java" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "(class|interface)\\s*JJJ\\b" :tests ("class test:" "public class test implements Something") :not ("class testnot:" "public class testnot implements Something")) (:language "java" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\benum\\s+JJJ\\b" :tests ("enum test {" "public enum test {") :not ("enum testnot {")) (:language "java" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "@interface\\s+JJJ\\b" :tests ("@interface test {" "public @interface test {") :not ("@interface testnot {")) ;; grep/git-grep fallback: requires access modifier because grep lacks ;; PCRE features (?:...) and {n,m} used by the ag/rg rule above. (:language "java" :type "function" :supports ("grep" "git-grep") :regex "(public|protected|private|static)[^(]+JJJ\\s*\\\(" :tests ("public int test()" "private static String test(param)" "protected List test()") :not ("test()" "new test()")) ;;-- vala (again just like c#, exactly the same..) (:language "vala" :type "function" :supports ("ag" "rg") :regex "^\\s*(?:[\\w\\[\\]]+\\s+){1,3}JJJ\\s*\\\(" :tests ("int test()" "int test(param)" "static int test()" "static int test(param)" "public static MyType test()" "private virtual SomeType test(param)" "static int test()") :not ("test()" "testnot()" "blah = new test()")) (:language "vala" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*\\bJJJ\\s*=[^=\\n)]+" :tests ("int test = 1234") :not ("if test == 1234:" "int nottest = 44")) (:language "vala" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "(class|interface)\\s*JJJ\\b" :tests ("class test:" "public class test : IReadableChannel, I") :not ("class testnot:" "public class testnot : IReadableChannel, I")) ;;-- coq (renamed Rocq in March 2025) (:language "coq" :type "function" :supports ("ag" "rg" "grep" "git-grep") :regex "\\s*Variable\\s+JJJ\\b" :tests ("Variable test") :not ("Variable testx")) (:language "coq" :type "function" :supports ("ag" "rg" "grep" "git-grep") :regex "\\s*Inductive\\s+JJJ\\b" :tests ("Inductive test") :not ("Inductive testx")) (:language "coq" :type "function" :supports ("ag" "rg" "grep" "git-grep") :regex "\\s*Lemma\\s+JJJ\\b" :tests ("Lemma test") :not ("Lemma testx")) (:language "coq" :type "function" :supports ("ag" "rg" "grep" "git-grep") :regex "\\s*Definition\\s+JJJ\\b" :tests ("Definition test") :not ("Definition testx")) (:language "coq" :type "function" :supports ("ag" "rg" "grep" "git-grep") :regex "\\s*Hypothesis\\s+JJJ\\b" :tests ("Hypothesis test") :not ("Hypothesis testx")) (:language "coq" :type "function" :supports ("ag" "rg" "grep" "git-grep") :regex "\\s*Theorem\\s+JJJ\\b" :tests ("Theorem test") :not ("Theorem testx")) (:language "coq" :type "function" :supports ("ag" "rg" "grep" "git-grep") :regex "\\s*Fixpoint\\s+JJJ\\b" :tests ("Fixpoint test") :not ("Fixpoint testx")) (:language "coq" :type "function" :supports ("ag" "rg" "grep" "git-grep") :regex "\\s*Module\\s+JJJ\\b" :tests ("Module test") :not ("Module testx")) (:language "coq" :type "function" :supports ("ag" "rg" "git-grep") :regex "\\s*CoInductive\\s+JJJ\\b" :tests ("CoInductive test") :not ("CoInductive testx")) ;;-- python (:language "python" :type "variable" :supports ("ag" "rg") :regex "\\s*\\bJJJ\\b(\\s*:[^=\\n]+)?\\s*=[^=\\n]+" :tests ("test = 1234" "test: int = 1234" "test: int | None = 1234" "test: List[Optional[int]] = [1234, None]") :not ("if test == 1234:" "_test = 1234" "if (test := 3):" "while test:" "test['key'] = 1234")) (:language "python" :type "variable" :supports ("grep" "git-grep") :regex "\\s*\\bJJJ\\b(\\s*:[^=]+)?\\s*=[^=]+" :tests ("test = 1234" "test: int = 1234" "test: int | None = 1234" "test: List[Optional[int]] = [1234, None]") :not ("if test == 1234:" "_test = 1234" "if (test := 3):" "while test:" "test['key'] = 1234")) (:language "python" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "def\\s*JJJ\\b\\s*\\\(" :tests ("\tdef test(asdf)" "def test()") :not ("\tdef testnot(asdf)" "def testnot()")) (:language "python" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "class\\s*JJJ\\b\\s*\\\(?" :tests ("class test(object):" "class test:") :not ("class testnot:" "class testnot(object):")) (:language "python" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "async\\s+def\\s*JJJ\\b\\s*\\\(" :tests ("async def test(param)" "async def test()") :not ("async def testnot(param)" "async def testnot()")) (:language "python" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*JJJ\\s*=\\s*lambda\\b" :tests ("test = lambda x: x" "test = lambda: None") :not ("testnot = lambda x: x" "foo(test=lambda x: x)")) ;;-- matlab (:language "matlab" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*\\bJJJ\\s*=[^=\\n]+" :tests ("test = 1234") :not ("for test = 1:2:" "_test = 1234")) (:language "matlab" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*function\\s*[^=]+\\s*=\\s*JJJ\\b" :tests ("\tfunction y = test(asdf)" "function x = test()" "function [x, losses] = test(A, y, lambda, method, qtile)") :not ("\tfunction testnot(asdf)" "function testnot()")) (:language "matlab" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*classdef\\s*JJJ\\b\\s*" :tests ("classdef test") :not ("classdef testnot")) ;;-- nim (:language "nim" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "(const|let|var)\\s*JJJ\\*?\\s*(=|:)[^=:\\n]+" :tests ("let test = 1234" "var test = 1234" "var test: Stat" "const test = 1234" "const test* = 1234") :not ("if test == 1234:")) (:language "nim" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "(proc|func|macro|template)\\s*`?JJJ`?\\b\\*?\\s*\\\(" :tests ("\tproc test(asdf)" "proc test()" "func test()" "macro test()" "template test()" "proc test*()") :not ("\tproc testnot(asdf)" "proc testnot()")) (:language "nim" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "type\\s*JJJ\\b\\*?\\s*(\\{[^}]+\\})?\\s*=\\s*\\w+" :tests ("type test = object" "type test {.pure.} = enum" "type test* = ref object") :not ("type testnot = object")) ;;-- nix (:language "nix" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\b\\s*JJJ\\s*=[^=;]+" :tests ("test = 1234;" "test = 123;" "test=123") :not ("testNot = 1234;" "Nottest = 1234;" "AtestNot = 1234;")) ;;-- org ;; org named blocks (:language "org" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*#\\+[nN][aA][mM][eE]:\\s*JJJ\\j" :tests ("#+name: test" "#+NAME: test" "#+Name: test" " #+name: test" "#+name:test") :not ("#+name: tester" "#+name: test-block" "some #+name: test")) ;; org call references (#+call: block-name) (:language "org" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*#\\+[cC][aA][lL][lL]:\\s*JJJ\\j" :tests ("#+call: test" "#+CALL: test" "#+Call: test" " #+call: test" "#+call: test()" "#+call: test(x=1)") :not ("#+call: tester" "#+call: test-other" "some #+call: test")) ;; org headings (* Heading text) (:language "org" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\*+\\s+JJJ\\j" :tests ("* test" "** test" "*** test" "**** test" "* test heading" "** test with more words") :not ("* tester" "** testing" "* test-other" "not a * test heading")) ;; org custom ID (:CUSTOM_ID: value in property drawers) (:language "org" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*:CUSTOM_ID:\\s*JJJ\\j" :tests (":CUSTOM_ID: test" ":CUSTOM_ID: test" " :CUSTOM_ID: test" ":CUSTOM_ID:test") :not (":CUSTOM_ID: tester" ":CUSTOM_ID: test-other" "inline :CUSTOM_ID: test text")) ;;-- ruby (:language "ruby" :type "variable" :supports ("ag" "rg" "git-grep") :regex "^\\s*((\\w+[.])*\\w+,\\s*)*JJJ(,\\s*(\\w+[.])*\\w+)*\\s*=([^=>~]|$)" :tests ("test = 1234" "self.foo, test, bar = args") :not ("if test == 1234" "foo_test = 1234")) (:language "ruby" :type "function" :supports ("ag" "rg" "git-grep") :regex "(^|[^\\w.])((private|public|protected)\\s+)?def\\s+(\\w+(::|[.]))*JJJ($|[^\\w|:])" :tests ("def test(foo)" "def test()" "def test foo" "def test; end" "def self.test()" "def MODULE::test()" "private def test") :not ("def test_foo")) (:language "ruby" :type "function" :supports ("ag" "rg" "git-grep") :regex "(^|\\W)define(_singleton|_instance)?_method(\\s|[(])\\s*:JJJ($|[^\\w|:])" :tests ("define_method(:test, &body)" "mod.define_instance_method(:test) { body }")) (:language "ruby" :type "type" :supports ("ag" "rg" "git-grep") :regex "(^|[^\\w.])class\\s+(\\w*::)*JJJ($|[^\\w|:])" :tests ("class test" "class Foo::test")) (:language "ruby" :type "type" :supports ("ag" "rg" "git-grep") :regex "(^|[^\\w.])module\\s+(\\w*::)*JJJ($|[^\\w|:])" :tests ("module test" "module Foo::test")) (:language "ruby" :type "function" :supports ("ag" "rg" "git-grep") :regex "(^|\\W)alias(_method)?\\W+JJJ(\\W|$)" :tests ("alias test some_method" "alias_method :test, :some_method" "alias_method 'test' 'some_method'" "some_class.send(:alias_method, :test, :some_method)") :not ("alias some_method test" "alias_method :some_method, :test" "alias test_foo test")) ;;-- Groovy (:language "groovy" :type "variable" :supports ("ag" "rg" "git-grep") :regex "^\\s*((\\w+[.])*\\w+,\\s*)*JJJ(,\\s*(\\w+[.])*\\w+)*\\s*=([^=>~]|$)" :tests ("test = 1234" "self.foo, test, bar = args") :not ("if test == 1234" "foo_test = 1234")) (:language "groovy" :type "function" :supports ("ag" "rg" "git-grep") :regex "(^|[^\\w.])((private|public)\\s+)?def\\s+(\\w+(::|[.]))*JJJ($|[^\\w|:])" :tests ("def test(foo)" "def test()" "def test foo" "def test; end" "def self.test()" "def MODULE::test()" "private def test") :not ("def test_foo")) (:language "groovy" :type "type" :supports ("ag" "rg" "git-grep") :regex "(^|[^\\w.])class\\s+(\\w*::)*JJJ($|[^\\w|:])" :tests ("class test" "class Foo::test")) ;;-- crystal (:language "crystal" :type "variable" :supports ("ag" "rg" "git-grep") :regex "^\\s*((\\w+[.])*\\w+,\\s*)*JJJ(,\\s*(\\w+[.])*\\w+)*\\s*=([^=>~]|$)" :tests ("test = 1234" "self.foo, test, bar = args") :not ("if test == 1234" "foo_test = 1234")) (:language "crystal" :type "function" :supports ("ag" "rg" "git-grep") :regex "(^|[^\\w.])((private|public|protected)\\s+)?def\\s+(\\w+(::|[.]))*JJJ($|[^\\w|:])" :tests ("def test(foo)" "def test()" "def test foo" "def test; end" "def self.test()" "def MODULE::test()" "private def test") :not ("def test_foo")) (:language "crystal" :type "type" :supports ("ag" "rg" "git-grep") :regex "(^|[^\\w.])class\\s+(\\w*::)*JJJ($|[^\\w|:])" :tests ("class test" "class Foo::test")) (:language "crystal" :type "type" :supports ("ag" "rg" "git-grep") :regex "(^|[^\\w.])module\\s+(\\w*::)*JJJ($|[^\\w|:])" :tests ("module test" "module Foo::test")) (:language "crystal" :type "type" :supports ("ag" "rg" "git-grep") :regex "(^|[^\\w.])struct\\s+(\\w*::)*JJJ($|[^\\w|:])" :tests ("struct test" "struct Foo::test")) (:language "crystal" :type "type" :supports ("ag" "rg" "git-grep") :regex "(^|[^\\w.])alias\\s+(\\w*::)*JJJ($|[^\\w|:])" :tests ("alias test" "alias Foo::test")) ;;-- scad (:language "scad" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*\\bJJJ\\s*=[^=\\n]+" :tests ("test = 1234") :not ("if test == 1234 {")) (:language "scad" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "function\\s*JJJ\\s*\\\(" :tests ("function test()" "function test ()")) (:language "scad" :type "module" :supports ("ag" "grep" "rg" "git-grep") :regex "module\\s*JJJ\\s*\\\(" :tests ("module test()" "module test ()")) ;;-- scala (:language "scala" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bval\\s*JJJ\\s*=[^=\\n]+" :tests ("val test = 1234") :not ("case test => 1234")) (:language "scala" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bvar\\s*JJJ\\s*=[^=\\n]+" :tests ("var test = 1234") :not ("case test => 1234")) (:language "scala" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\btype\\s*JJJ\\s*=[^=\\n]+" :tests ("type test = 1234") :not ("case test => 1234")) (:language "scala" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bdef\\s*JJJ\\s*\\\(" :tests ("def test(asdf)" "def test()")) (:language "scala" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "class\\s*JJJ\\s*\\\(?" :tests ("class test(object)")) (:language "scala" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "trait\\s*JJJ\\s*\\\(?" :tests ("trait test(object)")) (:language "scala" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "object\\s*JJJ\\s*\\\(?" :tests ("object test(object)")) ;;-- solidity (:language "solidity" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "function\\s*JJJ\\s*\\\(" :tests ("function test() internal" "function test (uint x, address y)" "function test() external")) (:language "solidity" :type "modifier" :supports ("ag" "grep" "rg" "git-grep") :regex "modifier\\s*JJJ\\s*\\\(" :tests ("modifier test()" "modifier test ()")) (:language "solidity" :type "event" :supports ("ag" "grep" "rg" "git-grep") :regex "event\\s*JJJ\\s*\\\(" :tests ("event test();" "event test (uint indexed x)" "event test(uint x, address y)")) (:language "solidity" :type "error" :supports ("ag" "grep" "rg" "git-grep") :regex "error\\s*JJJ\\s*\\\(" :tests ("error test();" "error test (uint x)" "error test(uint x, address y)")) (:language "solidity" :type "contract" :supports ("ag" "grep" "rg" "git-grep") :regex "contract\\s*JJJ\\s*(is|\\\{)" :tests ("contract test{" "contract test {" "contract test is foo")) (:language "solidity" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "struct\\s*JJJ\\s*\\\{" :tests ("struct test{" "struct test {")) ;;-- R (:language "r" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*=[^=><]" :tests ("test = 1234") :not ("if (test == 1234)")) (:language "r" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*<-\\s*function\\b" :tests ("test <- function" "test <- function(") :not ("test <- functionX")) ;;-- perl (:language "perl" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "sub\\s*JJJ\\s*(\\{|\\()" :tests ("sub test{" "sub test {" "sub test(" "sub test (")) (:language "perl" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "JJJ\\s*=\\s*" :tests ("$test = 1234")) ;;-- Tcl (:language "tcl" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "proc\\s+JJJ\\s*\\{" :tests ("proc test{" "proc test {")) (:language "tcl" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "set\\s+JJJ" :tests ("set test 1234")) (:language "tcl" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "(variable|global)\\s+JJJ" :tests ("variable test" "global test")) ;;-- shell (:language "shell" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "function\\s*JJJ\\s*" :tests ("function test{" "function test {" "function test () {") :not ("function nottest {")) (:language "shell" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "JJJ\\\(\\\)\\s*\\{" :tests ("test() {") :not ("testx() {")) (:language "shell" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*=\\s*" :tests ("test = 1234") :not ("blahtest = 1234")) ;;-- php (:language "php" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "function\\s*JJJ\\s*\\\(" :tests ("function test()" "function test ()")) (:language "php" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\*\\s@method\\s+[^ \t]+\\s+JJJ\\(" :tests ("/** @method string|false test($a)" " * @method bool test()")) (:language "php" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "(\\s|->|\\$|::)JJJ\\s*=\\s*" :tests ("$test = 1234" "$foo->test = 1234")) (:language "php" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\*\\s@property(-read|-write)?\\s+([^ \t]+\\s+)&?\\$JJJ(\\s+|$)" :tests ("/** @property string $test" "/** @property string $test description for $test property" " * @property-read bool|bool $test" " * @property-write \\ArrayObject $test")) (:language "php" :type "trait" :supports ("ag" "grep" "rg" "git-grep") :regex "trait\\s*JJJ\\s*\\\{" :tests ("trait test{" "trait test {")) (:language "php" :type "interface" :supports ("ag" "grep" "rg" "git-grep") :regex "interface\\s*JJJ\\s*(extends|\\\{)" :tests ("interface test{" "interface test {") "interface test extends test2") (:language "php" :type "class" :supports ("ag" "grep" "rg" "git-grep") :regex "class\\s*JJJ\\s*(extends|implements|\\\{)" :tests ("class test{" "class test {" "class test extends foo" "class test implements foo")) ;; PHP 8.1+ enum definition (:language "php" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\benum\\s+JJJ\\b\\s*(:\\s*(int|string))?\\s*(implements[^{]+)?\\{" :tests ("enum test {" "enum test: int {" "enum test: string {" "enum test implements Stringable {" "enum test: int implements JsonSerializable {") :not ("// enum test" "$enum = 'test'")) ;; PHP 8.1+ enum case (:language "php" :type "constant" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*case\\s+JJJ\\s*(;|=)" :tests (" case test;" "case test = 1;" "case test = 'value';") :not ("case 'test':" "case \"test\":" "default:")) ;; PHP abstract class (:language "php" :type "class" :supports ("ag" "grep" "rg" "git-grep") :regex "\\babstract\\s+class\\s+JJJ\\s*(extends|implements|\\\{)" :tests ("abstract class test {" "abstract class test extends Base {" "abstract class test implements Foo {") :not ("// abstract class test")) ;; PHP final class (including final readonly) (:language "php" :type "class" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bfinal\\s+(readonly\\s+)?class\\s+JJJ\\s*(extends|implements|\\\{)" :tests ("final class test {" "final readonly class test {" "final class test extends Base {") :not ("// final class test")) ;; PHP readonly class (including readonly final) (:language "php" :type "class" :supports ("ag" "grep" "rg" "git-grep") :regex "\\breadonly\\s+(final\\s+)?class\\s+JJJ\\s*(extends|implements|\\\{)" :tests ("readonly class test {" "readonly final class test {" "readonly class test implements Serializable {") :not ("// readonly class test")) ;; PHP class constant with optional visibility/final (:language "php" :type "constant" :supports ("ag" "grep" "rg" "git-grep") :regex "(final\\s+)?(public|protected|private)?\\s*const\\s+JJJ\\s*=" :tests ("const test = 'value';" "public const test = 100;" "private const test = 'secret';" "protected const test = [];" "final public const test = 42;" "final const test = true;") :not ("$const = 'test'" "// const test")) ;; PHP global const (outside class) (:language "php" :type "constant" :supports ("ag" "grep" "rg" "git-grep") :regex "^const\\s+JJJ\\s*=" :tests ("const test = 'app_name';" "const test = 42;" "const test = [];") :not (" const test =" "public const test =")) ;; PHP define() function (:language "php" :type "constant" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*define\\s*\\(\\s*['\"]JJJ['\"]" :tests ("define('test', 100);" "define(\"test\", 'value');" "define('test',") :not ("// define('test'" "$define = 'test'")) ;; PHP typed property declaration (:language "php" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "(public|protected|private)\\s+(static\\s+)?(readonly\\s+)?\\??[A-Za-z_][A-Za-z0-9_\\\\|&]*\\s+\\$JJJ\\s*(;|=|,)" :tests ("public int $test;" "private string $test = '';" "protected ?array $test = null;" "public readonly string $test;" "private static int $test = 0;" "public DateTime $test;") :not ("public function foo(int $test)" "return $test;")) ;; PHP static property (without explicit type) (:language "php" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "(public|protected|private)?\\s*static\\s+\\$JJJ\\s*(;|=)" :tests ("static $test;" "public static $test = [];" "private static $test = null;") :not ("static function test()" "static::$test")) ;; PHP namespace declaration (:language "php" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bnamespace\\s+([A-Za-z_][A-Za-z0-9_]*\\\\)*JJJ\\s*[;{]" :tests ("namespace test;" "namespace App\\test;" "namespace App\\Services\\test {") :not ("// namespace test" "use App\\test;")) ;; PHP use statement for class imports (:language "php" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\buse\\s+([A-Za-z_][A-Za-z0-9_]*\\\\)*JJJ\\s*(;|,|\\s+as\\s+)" :tests ("use test;" "use App\\test;" "use App\\Models\\test as TestModel;" "use App\\test, App\\Other;") :not ("use function test;" "use const test;")) ;;-- dart (:language "dart" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*\\([^()]*\\)\\s*[{]" :tests ("test(foo) {" "test (foo){" "test(foo){")) (:language "dart" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "class\\s*JJJ\\s*[\\\(\\\{]" :tests ("class test(object) {" "class test{")) ;;-- dlang (:language "dlang" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "^[[:space:]]*([][[:alnum:]_!.*<>]+[[:space:]]+)+JJJ[[:space:]]*\\\([^;{}]*\\\)([[:space:]]+@?[[:alnum:]_]+)*[[:space:]]*[{]" :tests ("void test() {" "public static int test(int value) {" "auto test(T)(T value) {" "void test() pure {" "void test() @safe {") :not ("return test();" "auto value = test(arg);" "test();")) (:language "dlang" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*\\bJJJ\\s*=[^=\\n)]+" :tests ("int test = 1234;" "immutable test = 1;" "auto test = foo();") :not ("if (test == 1234)" "int nottest = 44;")) (:language "dlang" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "(class|interface|struct|union|enum|alias)\\s+JJJ\\b" :tests ("class test {" "struct test {" "alias test = int;") :not ("class testnot {" "alias nottest = test;")) ;;-- faust (:language "faust" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\(\\\(.+\\\)\)*\\s*=" :tests ("test = osc + 0.5;" "test(freq) = osc(freq) + 0.5;")) ;;-- fennel (:language "fennel" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\((local|var)\\s+JJJ\\j" :tests ("(local test (foo)" "(var test (foo)")) (:language "fennel" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\(fn\\s+JJJ\\j" :tests ("(fn test [foo]") :not ("(fn test? [foo]")) (:language "fennel" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\(macro\\s+JJJ\\j" :tests ("(macro test [foo]")) ;;-- fortran (:language "fortran" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*\\bJJJ\\s*=[^=\\n]+" :tests ("test = 1234") :not ("if (test == 1234)")) (:language "fortran" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\b(function|subroutine|FUNCTION|SUBROUTINE)\\s+JJJ\\b\\s*\\\(" :tests ("function test (foo)" "integer function test(foo)" "subroutine test (foo, bar)" "FUNCTION test (foo)" "INTEGER FUNCTION test(foo)" "SUBROUTINE test (foo, bar)") :not ("end function test" "end subroutine test" "END FUNCTION test" "END SUBROUTINE test")) (:language "fortran" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*(interface|INTERFACE)\\s+JJJ\\b" :tests ("interface test" "INTERFACE test") :not ("interface test2" "end interface test" "INTERFACE test2" "END INTERFACE test")) (:language "fortran" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*(module|MODULE)\\s+JJJ\\s*" :tests ("module test" "MODULE test") :not ("end module test" "END MODULE test")) ;;-- go (:language "go" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*\\bJJJ\\s*=[^=\\n]+" :tests ("test = 1234") :not ("if test == 1234 {")) (:language "go" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*\\bJJJ\\s*:=\\s*" :tests ("test := 1234")) (:language "go" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "func\\s+\\\([^\\\)]*\\\)\\s+JJJ\\s*\\\(" :tests ("func (s *blah) test(filename string) string {")) (:language "go" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "func\\s+JJJ\\s*\\\(" :tests ("func test(url string) (string, error)")) (:language "go" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "type\\s+JJJ\\s+struct\\s+\\\{" :tests ("type test struct {")) (:language "go" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "type\\s+JJJ(\\\[[^]]+\\\])?\\s+interface\\s*\\\{" :tests ("type test interface {" "type test[T comparable] interface {") :not ("type testnot interface {")) (:language "go" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "type\\s+JJJ(\\\[[^]]+\\\])?\\s+" :tests ("type test int" "type test[T any] []byte" "type test = string" "type test []byte" "type test string") :not ("type testnot int")) ;;-- javascript extended (:language "javascript" :tags ("angular") :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "(service|factory)\\\(['\"]JJJ['\"]" :tests ("module.factory('test', [\"$rootScope\", function($rootScope) {")) (:language "javascript" :tags ("es6") :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*[=:]\\s*\\\([^\\\)]*\\\)\\s+=>" :tests ("const test = (foo) => " "test: (foo) => {" " test: (foo) => {")) (:language "javascript" :tags ("es6") :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*\\([^()]*\\)\\s*[{]" :tests ("test(foo) {" "test (foo){" "test(foo){") :not ("test = blah.then(function(){")) (:language "javascript" :tags ("es6") :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "class\\s*JJJ\\s*[\\\(\\\{]" :tests ("class test(object) {" "class test{")) (:language "javascript" :tags ("es6") :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "class\\s*JJJ\\s+extends" :tests ("class test extends Component{")) ;;-- javascript (:language "javascript" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*\\bJJJ\\s*=[^=\\n]+" :tests ("test = 1234" "const test = props =>") :not ("if (test === 1234)")) (:language "javascript" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bfunction\\b[^\\(]*\\\(\\s*[^\\)]*\\bJJJ\\b\\s*,?\\s*\\\)?" :tests ("function (test)" "function (test, blah)" "function somefunc(test, blah) {" "function(blah, test)") :not ("function (testLen)" "function (test1, blah)" "function somefunc(testFirst, blah) {" "function(blah, testLast)" "function (Lentest)" "function (blahtest, blah)" "function somefunc(Firsttest, blah) {" "function(blah, Lasttest)")) (:language "javascript" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "function\\s*JJJ\\s*\\\(" :tests ("function test()" "function test ()")) (:language "javascript" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*:\\s*function\\s*\\\(" :tests ("test: function()")) (:language "javascript" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*=\\s*function\\s*\\\(" :tests ("test = function()")) ;;-- hcl terraform (:language "hcl" :type "block" :supports ("ag" "grep" "rg" "git-grep") :regex "(variable|output|module)\\s*\"JJJ\"\\s*\\\{" :tests ("variable \"test\" {" "output \"test\" {" "module \"test\" {")) (:language "hcl" :type "block" :supports ("ag" "grep" "rg" "git-grep") :regex "(data|resource)\\s*\"\\w+\"\\s*\"JJJ\"\\s*\\\{" :tests ("data \"openstack_images_image_v2\" \"test\" {" "resource \"google_compute_instance\" \"test\" {")) ;;-- typescript (:language "typescript" :tags ("angular") :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "(service|factory)\\\(['\"]JJJ['\"]" :tests ("module.factory('test', [\"$rootScope\", function($rootScope) {")) (:language "typescript" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*[=:]\\s*\\\([^\\\)]*\\\)\\s+=>" :tests ("const test = (foo) => " "test: (foo) => {" " test: (foo) => {")) (:language "typescript" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*\\([^()]*\\)\\s*[{]" :tests ("test(foo) {" "test (foo){" "test(foo){") :not ("test = blah.then(function(){")) (:language "typescript" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "class\\s*JJJ(\\s*<[^>]*>)?\\s*[\\\(\\\{]" :tests ("class test{" "class test{")) (:language "typescript" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "class\\s*JJJ(\\s*<[^>]*>)?\\s+extends" :tests ("class test extends Component{" "class test extends Component{")) (:language "typescript" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "class\\s*JJJ(\\s*<[^>]*>)?\\s*[\\\(\\\{]" :tests ("class test{" "class test{")) (:language "typescript" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "class\\s*JJJ(\\s*<[^>]*>)?\\s+extends" :tests ("class test extends Component{" "class test extends Component{")) (:language "typescript" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "(export\\s+)?interface\\s+JJJ\\b" :tests ("interface test{" "interface test extends Thing{" "export interface test{") :not ("interface testnot{")) (:language "typescript" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "(export\\s+)?type\\s+JJJ\\b" :tests ("type test = number" "type test = T" "export type test = number") :not ("type testnot = number")) (:language "typescript" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "(export\\s+)?enum\\s+JJJ\\b" :tests ("enum test{" "export enum test{") :not ("enum testnot{")) (:language "typescript" :type "module" :supports ("ag" "grep" "rg" "git-grep") :regex "(declare\\s+)?namespace\\s+JJJ\\b" :tests ("declare namespace test" "namespace test") :not ("declare testnot")) (:language "typescript" :type "module" :supports ("ag" "grep" "rg" "git-grep") :regex "(export\\s+)?module\\s+JJJ\\b" :tests ("export module test" "module test") :not ("module testnot")) (:language "typescript" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "function\\s*JJJ\\s*\\\(" :tests ("function test()" "function test ()")) (:language "typescript" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*:\\s*function\\s*\\\(" :tests ("test: function()")) (:language "typescript" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*=\\s*function\\s*\\\(" :tests ("test = function()")) (:language "typescript" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*\\bJJJ\\s*=[^=\\n]+" :tests ("test = 1234" "const test = props =>") :not ("if (test === 1234)")) (:language "typescript" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bfunction\\b[^\\(]*\\\(\\s*[^\\)]*\\bJJJ\\b\\s*,?\\s*\\\)?" :tests ("function (test)" "function (test, blah)" "function somefunc(test, blah) {" "function(blah, test)") :not ("function (testLen)" "function (test1, blah)" "function somefunc(testFirst, blah) {" "function(blah, testLast)" "function (Lentest)" "function (blahtest, blah)" "function somefunc(Firsttest, blah) {" "function(blah, Lasttest)")) ;;-- julia (:language "julia" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "(@noinline|@inline)?\\s*function\\s*JJJ(\\{[^\\}]*\\})?\\(" :tests ("function test()" "@inline function test()" "function test{T}(h)")) (:language "julia" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "(@noinline|@inline)?JJJ(\\{[^\\}]*\\})?\\([^\\)]*\\)\s*=" :tests ("test(a)=1" "test(a,b)=1*8" "@noinline test()=1" "test{T}(x)=x")) (:language "julia" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "macro\\s*JJJ\\(" :tests ("macro test(a)=1" " macro test(a,b)=1*8")) (:language "julia" :type "variable" :supports ("ag" "rg") :regex "const\\s+JJJ\\b" :tests ("const test = ")) (:language "julia" :type "type" :supports ("ag" "rg") :regex "(mutable)?\\s*struct\\s*JJJ" :tests ("struct test")) (:language "julia" :type "type" :supports ("ag" "rg") :regex "(type|immutable|abstract)\\s*JJJ" :tests ("type test" "immutable test" "abstract test <:Testable" )) ;;-- haskell (:language "haskell" :type "module" :supports ("ag" "rg" "grep" "git-grep") :regex "^module\\s+JJJ\\s+" :tests ("module test (exportA, exportB) where") :not ("-- module test" "import test")) ; TODO Doesn't support any '=' in arguments. E.g. 'foo A{a = b,..} = bar'. (:language "haskell" :type "top level function" :supports ("ag" "rg") :regex "^\\bJJJ(?!(\\s+::))\\s+((.|\\s)*?)=\\s+" :tests ("test n = n * 2" "test X{..} (Y a b c) \n bcd \n =\n x * y" "test ab cd e@Datatype {..} (Another thing, inTheRow) = \n undefined" "test = runRealBasedMode @ext @ctx identity identity" "test unwrap wrap nr@Naoeu {..} (Action action, specSpecs) = \n undefined") :not ("nottest n = n * 2" "let testnot x y = x * y" "test $ y z" "let test a o = mda" "test :: Sometype -> AnotherType aoeu kek = undefined")) (:language "haskell" :type "type-like" :supports ("ag" "rg" "grep" "git-grep") :regex "^\\s*((data(\\s+family)?)|(newtype)|(type(\\s+family)?))\\s+JJJ\\s+" :tests ("newtype test a = Something { b :: Kek }" "data test a b = Somecase a | Othercase b" "type family test (x :: *) (xs :: [*]) :: Nat where" "data family test " "type test = TestAlias") :not ("newtype nottest a = nottest (Not a)" "data testnot b = Aoeu")) ; datatype contstuctor that doesn't match type definition. (:language "haskell" :type "(data)type constructor 1" :supports ("ag" "rg") :regex "(data|newtype)\\s{1,3}(?!JJJ\\s+)([^=]{1,40})=((\\s{0,3}JJJ\\s+)|([^=]{0,500}?((?\\s*)?JJJ\\s+" :tests ( "class (Constr1 m, Constr 2) => test (Kek a) where" "class test (Veryovka a) where ") :not ("class test2 (Kek a) where" "class maketest (AoeuTest x y z) where")) ;;-- ocaml (:language "ocaml" :type "type" :supports ("ag" "rg" "grep" "git-grep") :regex "^\\s*(and|type)\\s+.*\\bJJJ\\b" :tests ("type test =" "and test =" "type 'a test =" "type ('a, _, 'c) test")) (:language "ocaml" :type "variable" :supports ("ag" "rg" "grep" "git-grep") :regex "let\\s+JJJ\\b" :tests ("let test =" "let test x y =")) (:language "ocaml" :type "variable" :supports ("ag" "rg" "grep" "git-grep") :regex "let\\s+rec\\s+JJJ\\b" :tests ("let rec test =" "let rec test x y =")) (:language "ocaml" :type "variable" :supports ("ag" "rg" "grep" "git-grep") :regex "\\s*val\\s*\\bJJJ\\b\\s*" :tests ("val test")) (:language "ocaml" :type "module" :supports ("ag" "rg" "grep" "git-grep") :regex "^\\s*module\\s*\\bJJJ\\b" :tests ("module test =")) (:language "ocaml" :type "module" :supports ("ag" "rg" "grep" "git-grep") :regex "^\\s*module\\s*type\\s*\\bJJJ\\b" :tests ("module type test =")) ;;-- purescript (:language "purescript" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*\\bJJJ\\b\\s+(::|[^=]*\\s=[^=])" :tests ("test :: Int -> String" "test :: Number -> Number -> Number" " test :: Int -> String" "test x y = x + y" "test (Just x) = x" "test { x } = x") :not ("nottest :: Int -> String" "testing x = 1" "test x == y" "test >>= bar")) (:language "purescript" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*\\bJJJ\\s*=[^=]" :tests ("test = 42" "test = \\x -> x + 1" " test = compute x") :not ("test == 42" "-- test = 42")) (:language "purescript" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\blet\\s+JJJ\\s*(\\::|=[^=])" :tests ("let test = 42" "let test :: Int") :not ("let testing = 42" "let test == 42")) (:language "purescript" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "^\\s*(type|newtype|data)\\s+JJJ\\b" :tests ("type test = Int" "newtype test = test Int" "data test = test Int | Other String") :not ("type testOther = Int" "data testOther = testOther Int")) ;; lua (:language "lua" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*\\bJJJ\\s*=[^=\\n]+" :tests ("test = 1234") :not ("if test === 1234")) (:language "lua" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bfunction\\b[^\\(]*\\\(\\s*[^\\)]*\\bJJJ\\b\\s*,?\\s*\\\)?" :tests ("function (test)" "function (test, blah)" "function somefunc(test, blah)" "function(blah, test)") :not ("function (testLen)" "function (test1, blah)" "function somefunc(testFirst, blah)" "function(blah, testLast)" "function (Lentest)" "function (blahtest, blah)" "function somefunc(Firsttest, blah)" "function(blah, Lasttest)")) (:language "lua" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "function\\s*JJJ\\s*\\\(" :tests ("function test()" "function test ()")) (:language "lua" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "function\\s*.+[.:]JJJ\\s*\\\(" :tests ("function MyClass.test()" "function MyClass.test ()" "function MyClass:test()" "function MyClass:test ()")) (:language "lua" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*=\\s*function\\s*\\\(" :tests ("test = function()")) (:language "lua" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\b.+\\.JJJ\\s*=\\s*function\\s*\\\(" :tests ("MyClass.test = function()")) ;;-- rust (:language "rust" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\blet\\s+(\\\([^=\\n]*)?(mut\s+)?JJJ([^=\\n]*\\\))?(:\\s*[^=\\n]+)?\\s*=\\s*[^=\\n]+" :tests ("let test = 1234;" "let test: u32 = 1234;" "let test: Vec = Vec::new();" "let mut test = 1234;" "let mut test: Vec = Vec::new();" "let (a, test, b) = (1, 2, 3);" "let (a, mut test, mut b) = (1, 2, 3);" "let (mut a, mut test): (u32, usize) = (1, 2);")) (:language "rust" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bconst\\s+JJJ:\\s*[^=\\n]+\\s*=[^=\\n]+" :tests ("const test: u32 = 1234;")) (:language "rust" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bstatic\\s+(mut\\s+)?JJJ:\\s*[^=\\n]+\\s*=[^=\\n]+" :tests ("static test: u32 = 1234;" "static mut test: u32 = 1234;")) ;; variable in method signature (:language "rust" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bfn\\s+.+\\s*\\\((.+,\\s+)?JJJ:\\s*[^=\\n]+\\s*(,\\s*.+)*\\\)" :tests ("fn abc(test: u32) -> u32 {" "fn abc(x: u32, y: u32, test: Vec, z: Vec)" "fn abc(x: u32, y: u32, test: &mut Vec, z: Vec)")) ;; "if let" and "while let" desugaring (:language "rust" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "(if|while)\\s+let\\s+([^=\\n]+)?(mut\\s+)?JJJ([^=\\n\\\(]+)?\\s*=\\s*[^=\\n]+" :tests ("if let Some(test) = abc() {" "if let Some(mut test) = abc() {" "if let Ok(test) = abc() {" "if let Ok(mut test) = abc() {" "if let Foo(mut test) = foo {" "if let test = abc() {" "if let Some(test) = abc()" "if let Some((a, test, b)) = abc()" "while let Some(test) = abc() {" "while let Some(mut test) = abc() {" "while let Ok(test) = abc() {" "while let Ok(mut test) = abc() {") :not ("while let test(foo) = abc() {")) ;; structure fields (:language "rust" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "struct\\s+[^\\n{]+[{][^}]*(\\s*JJJ\\s*:\\s*[^\\n},]+)[^}]*}" :tests ("struct Foo { abc: u32, test: Vec, b: PathBuf }" "struct Foo{test:Vec}" "struct FooBar<'a> { test: Vec }") :not ("struct Foo { abc: u32, b: Vec }" "/// ... construct the equivalent ...\nfn abc() {\n")) ;; enum variants (:language "rust" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "enum\\s+[^\\n{]+\\s*[{][^}]*\\bJJJ\\b[^}]*}" :tests ("enum Foo { VariantA, test, VariantB(u32) }" "enum Foo { test(T) }" "enum BadStyle{test}" "enum Foo32 { Bar, testing, test(u8) }") :not ("enum Foo { testing }")) (:language "rust" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bfn\\s+JJJ\\s*\\\(" :tests ("fn test(asdf: u32)" "fn test()" "pub fn test()")) (:language "rust" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bmacro_rules!\\s+JJJ" :tests ("macro_rules! test")) (:language "rust" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "struct\\s+JJJ\\s*[{\\\(]?" :tests ("struct test(u32, u32)" "struct test;" "struct test { abc: u32, def: Vec }")) (:language "rust" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "trait\\s+JJJ\\s*[{]?" :tests ("trait test;" "trait test { fn abc() -> u32; }")) (:language "rust" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\btype\\s+JJJ([^=\\n]+)?\\s*=[^=\\n]+;" :tests ("type test = Rc>;" "type test = Arc>>;")) (:language "rust" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "impl\\s+((\\w+::)*\\w+\\s+for\\s+)?(\\w+::)*JJJ\\s+[{]?" :tests ("impl test {" "impl abc::test {" "impl std::io::Read for test {" "impl std::io::Read for abc::test {")) (:language "rust" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "mod\\s+JJJ\\s*[{]?" :tests ("mod test;" "pub mod test {")) ;; enum definitions (:language "rust" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\benum\\s+JJJ\\b\\s*[{]?" :tests ("enum test { A, B, C }" "enum test {" "pub enum test {" "pub enum test { A, B }" "pub enum test {" "enum test { Variant1, Variant2(Type) }" "pub(crate) enum test { Variant }") :not ("enum testing { A, B }" "enum Foo { test, Bar }")) ;;-- elixir (:language "elixir" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bdef(p)?\\s+JJJ\\s*[ ,\\\(]" :tests ("def test do" "def test, do:" "def test() do" "def test(), do:" "def test(foo, bar) do" "def test(foo, bar), do:" "defp test do" "defp test(), do:")) (:language "elixir" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*JJJ\\s*=[^=\\n]+" :tests ("test = 1234") :not ("if test == 1234")) (:language "elixir" :type "module" :supports ("ag" "grep" "rg" "git-grep") :regex "defmodule\\s+(\\w+\\.)*JJJ\\s+" :tests ("defmodule test do" "defmodule Foo.Bar.test do")) (:language "elixir" :type "module" :supports ("ag" "grep" "rg" "git-grep") :regex "defprotocol\\s+(\\w+\\.)*JJJ\\s+" :tests ("defprotocol test do" "defprotocol Foo.Bar.test do")) ;;-- erlang (:language "erlang" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "^JJJ\\b\\s*\\\(" :tests ("test() ->" "test()->" "test(Foo) ->" "test (Foo,Bar) ->" "test(Foo, Bar)->")) (:language "erlang" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*JJJ\\s*=[^:=\\n]+" :tests ("test = 1234") :not ("if test =:= 1234" "if test == 1234")) (:language "erlang" :type "module" :supports ("ag" "grep" "rg" "git-grep") :regex "^-module\\\(JJJ\\\)" :tests ("-module(test).")) ;;-- scss (:language "scss" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "@mixin\\sJJJ\\b\\s*\\\(" :tests ("@mixin test()")) (:language "scss" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "@function\\sJJJ\\b\\s*\\\(" :tests ("@function test()")) (:language "scss" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "JJJ\\s*:\\s*" :tests ("test :")) ;;-- sml (:language "sml" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*(data)?type\\s+.*\\bJJJ\\b" :tests ("datatype test =" "datatype test=" "datatype 'a test =" "type test =" "type 'a test =" "type 'a test" "type test") :not ("datatypetest =")) (:language "sml" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*val\\s+\\bJJJ\\b" :tests ("val test =" "val test=" "val test : bool")) (:language "sml" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*fun\\s+\\bJJJ\\b.*\\s*=" :tests ("fun test list =" "fun test (STRING_NIL, a) =" "fun test ((s1,s2): 'a queue) : 'a * 'a queue =" "fun test (var : q) : int =" "fun test f e xs =")) (:language "sml" :type "module" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*(structure|signature|functor)\\s+\\bJJJ\\b" :tests ("structure test =" "structure test : MYTEST =" "signature test =" "functor test (T:TEST) =" "functor test(T:TEST) =")) ;;-- sql (:language "sql" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "(CREATE|create)\\s+(.+?\\s+)?(FUNCTION|function|PROCEDURE|procedure)\\s+JJJ\\s*\\\(" :tests ("CREATE FUNCTION test(i INT) RETURNS INT" "create or replace function test (int)" "CREATE PROCEDURE test (OUT p INT)" "create definer = 'test'@'localhost' procedure test()")) (:language "sql" :type "table" :supports ("ag" "grep" "rg" "git-grep") :regex "(CREATE|create)\\s+(.+?\\s+)?(TABLE|table)(\\s+(IF NOT EXISTS|if not exists))?\\s+JJJ\\b" :tests ("CREATE TABLE test (" "create temporary table if not exists test" "CREATE TABLE IF NOT EXISTS test (" "create global temporary table test")) (:language "sql" :type "view" :supports ("ag" "grep" "rg" "git-grep") :regex "(CREATE|create)\\s+(.+?\\s+)?(VIEW|view)\\s+JJJ\\b" :tests ("CREATE VIEW test (" "create sql security definer view test" "CREATE OR REPLACE VIEW test AS foo")) (:language "sql" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "(CREATE|create)\\s+(.+?\\s+)?(TYPE|type)\\s+JJJ\\b" :tests ("CREATE TYPE test" "CREATE OR REPLACE TYPE test AS foo (" "create type test as (")) ;;-- systemverilog (:language "systemverilog" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*class\\s+\\bJJJ\\b" :tests ("virtual class test;" "class test;" "class test extends some_class") :not ("virtual class testing;" "class test2;" "class some_test" "class some_class extends test")) (:language "systemverilog" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*task\\s+\\bJJJ\\b" :tests ("task test (" "task test(") :not ("task testing (" "task test2(")) (:language "systemverilog" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*\\bJJJ\\b\\s*=" :tests ("assign test =" "assign test=" "int test =" "int test=") :not ("assign testing =" "assign test2=")) (:language "systemverilog" :type "function" : supports ("ag" "rg" "git-grep") :regex "function\\s[^\\s]+\\s*\\bJJJ\\b" :tests ("function Matrix test ;" "function Matrix test;") :not ("function test blah")) ;; matches SV class handle declarations (:language "systemverilog" :type "function" :supports ("ag" "rg" "git-grep") :regex "^\\s*[^\\s]*\\s*[^\\s]+\\s+\\bJJJ\\b" :tests ("some_class_name test" " another_class_name test ;" "some_class test[];" "some_class #(1) test") :not ("test some_class_name" "class some_class extends test")) ;;-- vhdl (:language "vhdl" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*type\\s+\\bJJJ\\b" :tests ("type test is" "type test is") :not ("type testing is" "type test2 is")) (:language "vhdl" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*constant\\s+\\bJJJ\\b" :tests ("constant test :" "constant test:") :not ("constant testing " "constant test2:")) (:language "vhdl" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "function\\s*\"?JJJ\"?\\s*\\\(" :tests ("function test(signal)" "function test (signal)" "function \"test\" (signal)") :not ("function testing(signal")) ;;-- latex (:language "tex" :type "command" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\\.*newcommand\\\*?\\s*\\\{\\s*(\\\\)JJJ\\s*}" :tests ("\\newcommand{\\test}" "\\renewcommand{\\test}" "\\renewcommand*{\\test}" "\\newcommand*{\\test}" "\\renewcommand{ \\test }") :not("\\test" "test")) (:language "tex" :type "command" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\\.*newcommand\\\*?\\s*(\\\\)JJJ\\j" :tests ("\\newcommand\\test {}" "\\renewcommand\\test{}" "\\newcommand \\test") :not("\\test" "test")) (:language "tex" :type "length" : supports ("ag" "grep" "rg" "git-grep") :regex "\\\\(s)etlength\\s*\\\{\\s*(\\\\)JJJ\\s*}" :tests ("\\setlength { \\test}" "\\setlength{\\test}" "\\setlength{\\test}{morecommands}" ) :not("\\test" "test")) (:language "tex" :type "counter" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\\newcounter\\\{\\s*JJJ\\s*}" :tests ("\\newcounter{test}" ) :not("\\test" "test")) (:language "tex" :type "environment" :supports ("ag" "grep" "rg" "git-grep") :regex "\\\\.*newenvironment\\s*\\\{\\s*JJJ\\s*}" :tests ("\\newenvironment{test}" "\\newenvironment {test}{morecommands}" "\\lstnewenvironment{test}" "\\newenvironment {test}" ) :not("\\test" "test" )) ;;-- pascal (todo: var, type, const) (:language "pascal" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bfunction\\s+JJJ\\b" :tests (" function test : ")) (:language "pascal" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bprocedure\\s+JJJ\\b" :tests (" procedure test ; ")) ;;-- f# (:language "fsharp" :type "variable" :supports ("ag" "rg" "grep" "git-grep") :regex "let\\s+JJJ\\b.*\\\=" :tests ("let test = 1234" "let test() = 1234" "let test abc def = 1234") :not ("let testnot = 1234" "let testnot() = 1234" "let testnot abc def = 1234")) (:language "fsharp" :type "interface" :supports ("ag" "rg" "grep" "git-grep") :regex "member(\\b.+\\.|\\s+)JJJ\\b.*\\\=" :tests ("member test = 1234" "member this.test = 1234") :not ("member testnot = 1234" "member this.testnot = 1234")) (:language "fsharp" :type "type" :supports ("ag" "rg" "grep" "git-grep") :regex "type\\s+JJJ\\b.*\\\=" :tests ("type test = 1234") :not ("type testnot = 1234")) ;;-- kotlin (:language "kotlin" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "fun\\s*(<[^>]*>)?\\s*JJJ\\s*\\(" :tests ("fun test()" "fun test()")) (:language "kotlin" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "(val|var)\\s*JJJ\\b" :not ("val testval" "var testvar") :tests ("val test " "var test")) (:language "kotlin" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "(class|interface)\\s*JJJ\\b" :tests ("class test" "class test : SomeInterface" "interface test")) ;;-- zig (:language "zig" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "fn\\s+JJJ\\b" :tests ("fn test() void {" "fn test(a: i32) i32 {" "pub fn test(a: i32) i32 {" "export fn test(a: i32) i32 {" "extern \"c\" fn test(a: i32) i32 {" "inline fn test(a: i32) i32 {")) (:language "zig" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "(var|const)\\s+JJJ\\b" :tests ("const test: i32 = 3;" "var test: i32 = 3;" "pub const test: i32 = 3;")) ;;-- protobuf (:language "protobuf" :type "message" :supports ("ag" "grep" "rg" "git-grep") :regex "message\\s+JJJ\\s*\\\{" :tests ("message test{" "message test {")) (:language "protobuf" :type "enum" :supports ("ag" "grep" "rg" "git-grep") :regex "enum\\s+JJJ\\s*\\\{" :tests ("enum test{" "enum test {")) ;;-- apex (literally the same regexes as java) (:language "apex" :type "function" :supports ("ag" "rg") :regex "^\\s*(?:[\\w\\[\\]]+\\s+){1,3}JJJ\\s*\\\(" :tests ("int test()" "int test(param)" "static int test()" "static int test(param)" "public static MyType test()" "private virtual SomeType test(param)" "static int test()" "private foo[] test()") :not ("test()" "testnot()" "blah = new test()" "foo bar = test()")) (:language "apex" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*\\bJJJ\\s*=[^=\\n)]+" :tests ("int test = 1234") :not ("if test == 1234:" "int nottest = 44")) (:language "apex" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "(class|interface)\\s*JJJ\\b" :tests ("class test:" "public class test implements Something") :not ("class testnot:" "public class testnot implements Something")) ;;-- jai (:language "jai" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*::" :tests ("test ::")) (:language "jai" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*(:|:\\s*=|::)" :tests ("test: Type" "test : Type = Val" "test :: Val")) (:language "jai" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*::" :tests ("test ::")) ;;-- odin (:language "odin" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "\\s*\\bJJJ\\s*:\\s*([^=\\n]+\\s*:|:|[^=\\n]+\\s*=|=)" :tests ("test :: struct" "test ::enum" "test:: union" "test: : custom_type" "test :: [2]f32" "test : f32 : 20" "test: i32 : 10" "test := 20" "test : f32 = 20" "test: i32 = 10" "test: i32= 10" "test :i32= 10" "test :: proc()" "test ::proc() {" "test:: proc(a: i32) -> i32 {" "test::proc{}" "test: :proc \"contextless\" {}")) (:language "odin" :type "function" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*:\\s*:\\s*proc\\s*[({]" :tests ("test :: proc(a: int)" "test ::proc()" "test:: proc(a: i32) -> i32 {" "test::proc{}") :not ("testnot :: proc()" "test: proc()")) (:language "odin" :type "type" :supports ("ag" "grep" "rg" "git-grep") :regex "\\bJJJ\\s*:\\s*:\\s*(struct|enum|union|bit_set|bit_field)\\b" :tests ("test :: struct {" "test :: enum {" "test :: union {" "test :: bit_set {" "test :: bit_field {") :not ("testnot :: struct {" "test :: structure_builder")) ;;-- cobol (:language "cobol" :type "variable" :supports ("ag" "grep" "rg" "git-grep") :regex "^(.{6}[^*/])?\\s*([frs]d|[[:digit:]]{1,2})\\s+JJJ([\\. ]|$)" :tests (" 01 test." "000350 10 test pic x." " 88 test value '10'." "fd test." " sd test.") :not (" *01 test" " 05 test-01" " 05 testing")) (:language "cobol" :type "section" :supports ("ag" "grep" "rg" "git-grep") :regex "^(.{6}[^*/])?JJJ(\\s+section)?\\." :tests (" test." " test section." "test." "test section.") :not (" test." " *test section.")) (:language "cobol" :type "submodule" :supports ("ag" "grep" "rg" "git-grep") :regex "^(.{6}[^*/])?program-id\\.\\s*[\"']?JJJ[\"']?.*\\." :tests (" program-id. test." " program-id. 'test'." "program-id. \"test\".") :not (" test."))) "List of search regex pattern templates organized by language and type. Used for generating the grep tool search commands. For a given language, a regular expression is only used by Dumb Jump when the currently used search tool is identified in the :supports value. The regex string is a template filled by `dumb-jump-populate-regex' and should contain the \"JJJ\" string, a marker for the searched identifier. It may also contain the following: - \"\\\\j\" : a marker for word-boundary regular-expression to use instead of \"\\\\b\" when word boundary concept must support Lisp language family and must not include the characters '-', '?' and '*'. In the Lisp family of programming languages a large number of characters are allowed in identifiers, including '-', '?' and '*'. This marker is replaced by value of the search tool specific dumb-jump-TOOL-word-boundary, like `dumb-jump-ag-word-boundary' for ag. - \"\\\\s\" : for a single white space character. Replaced by \"[[:space:]]\" for some search tools as identified by `dumb-jump-use-space-bracket-exp-for' returning t for a tool variant. The language types supported are listed in the linked page below: - Ag: URL \ `https://github.com/ggreer/the_silver_searcher/blob/master/tests/list_file_types.t' - rg: URL \ `https://github.com/BurntSushi/ripgrep/blob/master/crates/ignore/src/default_types.rs#L12' More information using the search tool command line help." :group 'dumb-jump :type '(repeat (plist :options ((:language string) (:type string) (:supports string) (:regex string) (:tests (repeat string)) (:not (repeat string)))))) (defcustom dumb-jump-language-file-exts ;; dumb-jump language file extension ag type rg type '((:language "elisp" :ext "el" :agtype "elisp" :rgtype "elisp") (:language "elisp" :ext "el.gz" :agtype "elisp" :rgtype "elisp") (:language "commonlisp" :ext "lisp" :agtype "lisp" :rgtype "lisp") (:language "commonlisp" :ext "lsp" :agtype "lisp" :rgtype "lisp") (:language "c++" :ext "c" :agtype "cc" :rgtype "c") (:language "c++" :ext "h" :agtype "cc" :rgtype "c") (:language "c++" :ext "C" :agtype "cpp" :rgtype "cpp") (:language "c++" :ext "H" :agtype "cpp" :rgtype "cpp") (:language "c++" :ext "tpp" :agtype "cpp" :rgtype nil) (:language "c++" :ext "cpp" :agtype "cpp" :rgtype "cpp") (:language "c++" :ext "hpp" :agtype "cpp" :rgtype "cpp") (:language "c++" :ext "cxx" :agtype "cpp" :rgtype "cpp") (:language "c++" :ext "hxx" :agtype "cpp" :rgtype nil) (:language "c++" :ext "cc" :agtype "cpp" :rgtype "cpp") (:language "c++" :ext "hh" :agtype "cpp" :rgtype "cpp") (:language "c++" :ext "c++" :agtype nil :rgtype nil) (:language "c++" :ext "h++" :agtype nil :rgtype nil) (:language "c++" :ext "ino" :agtype "cpp" :rgtype nil) (:language "coq" :ext "v" :agtype nil :rgtype nil) (:language "ocaml" :ext "ml" :agtype "ocaml" :rgtype "ocaml") (:language "ocaml" :ext "mli" :agtype "ocaml" :rgtype "ocaml") (:language "ocaml" :ext "mll" :agtype "ocaml" :rgtype "ocaml") (:language "ocaml" :ext "mly" :agtype "ocaml" :rgtype "ocaml") ;; groovy is nil type because jenkinsfile is not in searcher type lists (:language "groovy" :ext "gradle" :agtype nil :rgtype nil) (:language "groovy" :ext "groovy" :agtype nil :rgtype nil) (:language "groovy" :ext "jenkinsfile" :agtype nil :rgtype nil) (:language "haskell" :ext "hs" :agtype "haskell" :rgtype "haskell") (:language "haskell" :ext "lhs" :agtype "haskell" :rgtype "haskell") (:language "objc" :ext "m" :agtype "objc" :rgtype "objc") (:language "csharp" :ext "cs" :agtype "csharp" :rgtype "csharp") (:language "java" :ext "java" :agtype "java" :rgtype "java") (:language "vala" :ext "vala" :agtype "vala" :rgtype "vala") (:language "vala" :ext "vapi" :agtype "vala" :rgtype "vala") (:language "julia" :ext "jl" :agtype "julia" :rgtype "julia") (:language "clojure" :ext "clj" :agtype "clojure" :rgtype "clojure") (:language "clojure" :ext "cljc" :agtype "clojure" :rgtype "clojure") (:language "clojure" :ext "cljs" :agtype "clojure" :rgtype "clojure") (:language "clojure" :ext "cljx" :agtype "clojure" :rgtype "clojure") (:language "coffeescript" :ext "coffee" :agtype "coffee" :rgtype "coffeescript") (:language "faust" :ext "dsp" :agtype nil :rgtype nil) (:language "faust" :ext "lib" :agtype nil :rgtype nil) (:language "fennel" :ext "fnl" :agtype nil :rgtype nil) (:language "fortran" :ext "F" :agtype "fortran" :rgtype "fortran") (:language "fortran" :ext "f" :agtype "fortran" :rgtype "fortran") (:language "fortran" :ext "f77" :agtype "fortran" :rgtype "fortran") (:language "fortran" :ext "f90" :agtype "fortran" :rgtype "fortran") (:language "fortran" :ext "f95" :agtype "fortran" :rgtype "fortran") (:language "fortran" :ext "F77" :agtype "fortran" :rgtype "fortran") (:language "fortran" :ext "F90" :agtype "fortran" :rgtype "fortran") (:language "fortran" :ext "F95" :agtype "fortran" :rgtype "fortran") (:language "fortran" :ext "f03" :agtype "fortran" :rgtype "fortran") (:language "fortran" :ext "for" :agtype "fortran" :rgtype "fortran") (:language "fortran" :ext "ftn" :agtype "fortran" :rgtype "fortran") (:language "fortran" :ext "fpp" :agtype "fortran" :rgtype "fortran") (:language "go" :ext "go" :agtype "go" :rgtype "go") (:language "javascript" :ext "js" :agtype "js" :rgtype "js") (:language "javascript" :ext "jsx" :agtype "js" :rgtype "js") (:language "javascript" :ext "vue" :agtype "js" :rgtype "js") (:language "javascript" :ext "html" :agtype "html" :rgtype "html") (:language "javascript" :ext "css" :agtype "css" :rgtype "css") (:language "typescript" :ext "ts" :agtype "ts" :rgtype "ts") (:language "typescript" :ext "tsx" :agtype "ts" :rgtype "ts") (:language "typescript" :ext "vue" :agtype "ts" :rgtype "ts") (:language "dart" :ext "dart" :agtype nil :rgtype "dart") (:language "dlang" :ext "d" :agtype "dlang" :rgtype "d") (:language "dlang" :ext "di" :agtype "dlang" :rgtype "d") (:language "lua" :ext "lua" :agtype "lua" :rgtype "lua") ;; the extension "m" is also used by obj-c so must use matlab-mode ;; since obj-c will win by file extension, but here for searcher types (:language "matlab" :ext "m" :agtype "matlab" :rgtype "matlab") (:language "nim" :ext "nim" :agtype "nim" :rgtype "nim") (:language "nix" :ext "nix" :agtype "nix" :rgtype "nix") (:language "org" :ext "org" :agtype nil :rgtype "org") (:language "perl" :ext "pl" :agtype "perl" :rgtype "perl") (:language "perl" :ext "pm" :agtype "perl" :rgtype "perl") (:language "perl" :ext "pm6" :agtype "perl" :rgtype nil) (:language "perl" :ext "perl" :agtype nil :rgtype "perl") (:language "perl" :ext "plh" :agtype nil :rgtype "perl") (:language "perl" :ext "plx" :agtype nil :rgtype "perl") (:language "perl" :ext "pod" :agtype "perl" :rgtype "pod") (:language "perl" :ext "t" :agtype "perl" :rgtype nil) (:language "php" :ext "php" :agtype "php" :rgtype "php") (:language "php" :ext "php3" :agtype "php" :rgtype "php") (:language "php" :ext "php4" :agtype "php" :rgtype "php") (:language "php" :ext "php5" :agtype "php" :rgtype "php") (:language "php" :ext "phtml" :agtype "php" :rgtype "php") (:language "php" :ext "inc" :agtype "php" :rgtype nil) (:language "purescript" :ext "purs" :agtype nil :rgtype nil) (:language "python" :ext "py" :agtype "python" :rgtype "py") (:language "r" :ext "R" :agtype "r" :rgtype "r") (:language "r" :ext "r" :agtype "r" :rgtype "r") (:language "r" :ext "Rmd" :agtype "r" :rgtype "r") (:language "r" :ext "Rnw" :agtype "r" :rgtype "r") (:language "r" :ext "Rtex" :agtype "r" :rgtype nil) (:language "r" :ext "Rrst" :agtype "r" :rgtype nil) (:language "racket" :ext "rkt" :agtype "racket" :rgtype "lisp") (:language "crystal" :ext "cr" :agtype "crystal" :rgtype "crystal") (:language "crystal" :ext "ecr" :agtype "crystal" :rgtype nil) (:language "ruby" :ext "rb" :agtype "ruby" :rgtype "ruby") (:language "ruby" :ext "erb" :agtype "ruby" :rgtype nil) (:language "ruby" :ext "haml" :agtype "ruby" :rgtype nil) (:language "ruby" :ext "rake" :agtype "ruby" :rgtype nil) (:language "ruby" :ext "slim" :agtype "ruby" :rgtype nil) (:language "rust" :ext "rs" :agtype "rust" :rgtype "rust") (:language "zig" :ext "zig" :agtype nil :rgtype "zig") (:language "scad" :ext "scad" :agtype nil :rgtype nil) (:language "scala" :ext "scala" :agtype "scala" :rgtype "scala") (:language "scheme" :ext "scm" :agtype "scheme" :rgtype "lisp") (:language "scheme" :ext "ss" :agtype "scheme" :rgtype "lisp") (:language "scheme" :ext "sld" :agtype "scheme" :rgtype "lisp") (:language "janet" :ext "janet" :agtype "janet" :rgtype "lisp") (:language "shell" :ext "sh" :agtype nil :rgtype nil) (:language "shell" :ext "bash" :agtype nil :rgtype nil) (:language "shell" :ext "csh" :agtype nil :rgtype nil) (:language "shell" :ext "ksh" :agtype nil :rgtype nil) (:language "shell" :ext "tcsh" :agtype nil :rgtype nil) (:language "sml" :ext "sml" :agtype "sml" :rgtype "sml") (:language "solidity" :ext "sol" :agtype nil :rgtype nil) (:language "sql" :ext "sql" :agtype "sql" :rgtype "sql") (:language "swift" :ext "swift" :agtype nil :rgtype "swift") (:language "tex" :ext "tex" :agtype "tex" :rgtype "tex") (:language "elixir" :ext "ex" :agtype "elixir" :rgtype "elixir") (:language "elixir" :ext "exs" :agtype "elixir" :rgtype "elixir") (:language "elixir" :ext "eex" :agtype "elixir" :rgtype "elixir") (:language "erlang" :ext "erl" :agtype "erlang" :rgtype "erlang") (:language "systemverilog" :ext "sv" :agtype "verilog" :rgtype "verilog") (:language "systemverilog" :ext "svh" :agtype "verilog" :rgtype "verilog") (:language "vhdl" :ext "vhd" :agtype "vhdl" :rgtype "vhdl") (:language "vhdl" :ext "vhdl" :agtype "vhdl" :rgtype "vhdl") (:language "scss" :ext "scss" :agtype "css" :rgtype "css") (:language "pascal" :ext "pas" :agtype "delphi" :rgtype nil) (:language "pascal" :ext "dpr" :agtype "delphi" :rgtype nil) (:language "pascal" :ext "int" :agtype "delphi" :rgtype nil) (:language "pascal" :ext "dfm" :agtype "delphi" :rgtype nil) (:language "fsharp" :ext "fs" :agtype "fsharp" :rgtype nil) (:language "fsharp" :ext "fsi" :agtype "fsharp" :rgtype nil) (:language "fsharp" :ext "fsx" :agtype "fsharp" :rgtype nil) (:language "kotlin" :ext "kt" :agtype "kotlin" :rgtype "kotlin") (:language "kotlin" :ext "kts" :agtype "kotlin" :rgtype "kotlin") (:language "protobuf" :ext "proto" :agtype "proto" :rgtype "protobuf") (:language "hcl" :ext "tf" :agtype "terraform" :rgtype "tf") (:language "hcl" :ext "tfvars" :agtype "terraform" :rgtype nil) (:language "apex" :ext "cls" :agtype nil :rgtype nil) (:language "apex" :ext "trigger" :agtype nil :rgtype nil) (:language "jai" :ext "jai" :agtype nil :rgtype nil) (:language "odin" :ext "odin" :agtype nil :rgtype nil) (:language "cobol" :ext "cbl" :agtype nil :rgtype nil) (:language "cobol" :ext "cob" :agtype nil :rgtype nil) (:language "cobol" :ext "cpy" :agtype nil :rgtype nil)) "Map programming language to file extensions support by search tool. The value of :agytpe, :rgtype is nil when the search tool file type does not search for that file extension, otherwise it is the name of the file type for that tool." :group 'dumb-jump :type '(repeat (plist :options ((:language (string :tag "Language")) (:ext (string :tag "Extension")) (:agtype (string :tag "Ag type")) (:rgtype (string :tag "Ripgrep type")))))) (defcustom dumb-jump-language-contexts '((:language "javascript" :type "function" :left nil :right "^(" ) (:language "javascript" :type "variable" :left "($" :right nil ) (:language "javascript" :type "variable" :left "($" :right "^)" ) (:language "javascript" :type "variable" :left nil :right "^\\." ) (:language "javascript" :type "variable" :left nil :right "^;" ) (:language "typescript" :type "function" :left nil :right "^(" ) (:language "typescript" :type "type" :left "\\(?:[:<>,|&]\\|\\_<\\(?:as\\|extends\\|implements\\)\\_>\\)\\s-*$" :right nil) (:language "typescript" :type "type" :left "\\_<\\(?:type\\|interface\\|enum\\|class\\)\\_>\\s-+$" :right nil) (:language "perl" :type "function" :left nil :right "^(" ) (:language "tcl" :type "function" :left "\\[$" :right nil) (:language "tcl" :type "function" :left "^\\s*$" :right nil) (:language "tcl" :type "variable" :left "\\$$" :right nil) (:language "php" :type "function" :left nil :right "^(" ) (:language "php" :type "class" :left "new\\s+" :right nil ) (:language "elisp" :type "function" :left "($" :right nil ) (:language "elisp" :type "variable" :left nil :right "^)" ) (:language "scheme" :type "function" :left "($" :right nil ) (:language "scheme" :type "variable" :left nil :right "^)" ) (:language "jai" :type "function" :left nil :right "\\s*(" ) (:language "jai" :type "type" :left "\\s*:\\s*" :right nil) (:language "cobol" :type "submodule" :left "call\\s+" :right nil ) (:language "cobol" :type "section" :left "perform\\s+" :right nil ) (:language "cobol" :type "section" :left "go to\\s+" :right nil )) "List of under points contexts for each language. This helps limit the number of regular expressions we use if we know that if there's a ( immediately to the right of a symbol then it's probably a function call." :group 'dumb-jump :type '(repeat (plist :options ((:language (string :tag "Language")) (:type (choice (const "function") (const "variable"))) (:left (choice (const :tag "Anything" nil) (string :tag "Regular expression"))) (:right (choice (const :tag "Anything" nil) (string :tag "Regular expression"))))))) (defcustom dumb-jump-project-denoters '(".dumbjump" ".projectile" ".git" ".hg" ".fslckout" ".bzr" "_darcs" ".svn" "Makefile" "PkgInfo" "-pkg.el" "_FOSSIL_" "Cargo.toml") "Files and directories that signify a directory is a project root. If any of these denoting files are present in project sub-directories (like Makefile) then you can either remove the culprit denoter from the list or create a .dumbjumpignore file in the project sub-directory." :group 'dumb-jump :type '(repeat (string :tag "Name"))) (defcustom dumb-jump-default-project "~" "Directory of default project to search within if a project root is not found." :group 'dumb-jump :type 'string) (defcustom dumb-jump-project nil "Directory of project to search within if normal denoters will not work. This should only be needed in the rarest of cases." :group 'dumb-jump :type 'string) (defcustom dumb-jump-rust-search-dependencies nil "When non-nil, automatically include Rust dependency source paths. Uses `cargo metadata' to discover crate source directories. Requires `cargo' to be installed and a Cargo.toml in the project." :group 'dumb-jump :type 'boolean) (with-no-warnings ; don't warn about long example line (defcustom dumb-jump-extra-search-paths-function nil "Function to compute extra search paths for jump-to-definition lookups. When non-nil, this function is called with two arguments: LANG (a string naming the programming language, or nil if undetected) and PROJ-ROOT (the project root directory). It should return a list of directory path strings to include in the search, or nil. Returned paths may be absolute or relative to PROJ-ROOT (relative entries are expanded via `expand-file-name'). Non-existent directories and non-string entries are silently ignored. This is useful for dynamically adding paths such as virtualenv site-packages, installed library sources, or SDK directories. Example: (setq dumb-jump-extra-search-paths-function (lambda (lang proj-root) (when (string= lang \"python\") (let* ((default-directory proj-root) (path (string-trim (shell-command-to-string \"poetry run python -c \\\"import site; print(site.getsitepackages()[0])\\\"\")))) (when (file-directory-p path) (list path))))))" :group 'dumb-jump :type '(choice (const :tag "None" nil) function))) (defcustom dumb-jump-before-jump-hook nil "Hooks called before jumping." :group 'dumb-jump :type 'hook) (defcustom dumb-jump-after-jump-hook nil "Hooks called after jumping." :group 'dumb-jump :type 'hook) (defcustom dumb-jump-aggressive nil "If `t` jump aggressively with the possibility of a false positive. If `nil` always show list of more than 1 match." :group 'dumb-jump :type 'boolean) (defcustom dumb-jump-debug nil "If `t` will print helpful debug information. Also ensure that `dumb-jump-quiet' is nil for debugging!" :group 'dumb-jump :type 'boolean) (defcustom dumb-jump-confirm-jump-to-modified-file t "Confirm before jumping to a modified file? If t, confirm before jumping to a modified file (which may lead to an inaccurate jump). If nil, jump without confirmation but print a warning." :group 'dumb-jump :type 'boolean) (defcustom dumb-jump-disable-obsolete-warnings nil "If non-nil, don't warn about using the legacy interface." :group 'dumb-jump :type 'boolean) ;; --------------------------------------------------------------------------- ;; Debugging Utilities ;; ------------------- (defun dumb-jump-message-prin1 (format-str &rest args) "Helper debugging function: generate a FORMAT-STR message with all ARGS." ;; Fist transform each of ARGS to string with `prin1-to-string' then ;; pass them as arguments to message by making a list used the third ;; parameter to apply. (apply 'message format-str (mapcar #'prin1-to-string args))) ;; --------------------------------------------------------------------------- ;; Search tool presence checkers ;; ----------------------------- (defvar dumb-jump--detected-env-problems nil "List of detected environment problems. Last detected problem is first in the list.") (defun dumb-jump-env-problem (problem) "Add PROBLEM string to `dumb-jump--detected-env-problems' list." (unless (member problem dumb-jump--detected-env-problems) (push problem dumb-jump--detected-env-problems))) ;;;###autoload (defun dumb-jump-show-detected-problems (keep) "Print all problems detected by dumb-jump environment in *Message*. Erase the accumulated list unless the command is issued with any prefix key (such as `\\[universal-argument]')." (interactive "P") (if dumb-jump--detected-env-problems (progn (message "-- Dumb-Jump Detected Environment Problems:") (dolist (problem (reverse dumb-jump--detected-env-problems)) (message problem)) (unless keep (setq dumb-jump--detected-env-problems nil))) (message "No problem recorded yet."))) ;; -- (defvar dumb-jump--ag-installed? 'unset) (defun dumb-jump-ag-installed? () "Return t if ag is installed." (if (eq dumb-jump--ag-installed? 'unset) (setq dumb-jump--ag-installed? (dumb-jump--contains-p "ag version" (shell-command-to-string (concat dumb-jump-ag-cmd " --version")))) dumb-jump--ag-installed?)) (defvar dumb-jump--git-grep-plus-ag-installed? 'unset) (defun dumb-jump-git-grep-plus-ag-installed? () "Return t if git grep and ag is installed." (if (eq dumb-jump--git-grep-plus-ag-installed? 'unset) (setq dumb-jump--git-grep-plus-ag-installed? (and (dumb-jump-git-grep-installed?) (dumb-jump-ag-installed?))) dumb-jump--git-grep-plus-ag-installed?)) (defvar dumb-jump--rg-installed? 'unset) (defun dumb-jump-rg-installed? () "Return t if rg 0.10 or later with PCRE2 support is installed. Return nil otherwise. In that case store diagnostics information in the variable `dumb-jump--detected-env-problems'." (if (eq dumb-jump--rg-installed? 'unset) (setq dumb-jump--rg-installed? (catch 'rg-problem (unless (executable-find dumb-jump-rg-cmd) (dumb-jump-env-problem (format "Ripgrep not found as specified in `dumb-jump-rg-cmd': %s" dumb-jump-rg-cmd)) (throw 'rg-problem nil)) (let* ((stdout (shell-command-to-string (concat dumb-jump-rg-cmd " --version"))) (has-version (string-match "ripgrep \\([0-9]+\\.[0-9]+\\).*" stdout))) (unless has-version (dumb-jump-env-problem "Can't detect Ripgrep version.") (throw 'rg-problem nil)) (unless (version<= "0.10" (match-string-no-properties 1 stdout)) (dumb-jump-env-problem "Ripgrep >= 0.10 is not available.") (throw 'rg-problem nil)) (unless (string-match "features:.*pcre2" stdout) (dumb-jump-env-problem "Ripgrep does not support PCRE2.") (throw 'rg-problem nil)) t))) dumb-jump--rg-installed?)) (defvar dumb-jump--git-grep-installed? 'unset) (defun dumb-jump-git-grep-installed? () "Return t if git-grep is installed." (if (eq dumb-jump--git-grep-installed? 'unset) (setq dumb-jump--git-grep-installed? (dumb-jump--contains-p "fatal: no pattern given" (shell-command-to-string (concat dumb-jump-git-grep-cmd)))) dumb-jump--git-grep-installed?)) (defvar dumb-jump--grep-installed? 'unset) (defun dumb-jump-grep-installed? () "Return non-nil if a grep is installed, nil otherwise. Return \\='gnu if GNU grep is installed, \\='bsd if BSD grep is installed." (if (eq dumb-jump--grep-installed? 'unset) (let* ((version (shell-command-to-string (concat dumb-jump-grep-cmd " --version"))) (variant (cond ((dumb-jump--match "GNU grep" version) 'gnu) ((dumb-jump--match "[0-9]+\\.[0-9]+" version) 'bsd) (t nil)))) (setq dumb-jump--grep-installed? variant)) dumb-jump--grep-installed?)) ;; --------------------------------------------------------------------------- ;; Dumb-Jump Tests ;; --------------- (defun dumb-jump-run-test (test cmd) "Use TEST as the standard input for the CMD." (with-temp-buffer (insert test) (shell-command-on-region (point-min) (point-max) cmd nil t) (buffer-substring-no-properties (point-min) (point-max)))) (defun dumb-jump-run-test-temp-file (test thefile realcmd) "Write TEST string to temporary file THEFILE, run shell REALCMD on it. Return resulting string." (with-temp-buffer (insert test) (write-file thefile nil) (delete-region (point-min) (point-max)) (shell-command realcmd t) (set-buffer-modified-p nil) ; prevent prompt on file deletion during test (delete-file thefile) (buffer-substring-no-properties (point-min) (point-max)))) (defun dumb-jump-run-git-grep-test (test git-grep-cmd) "Execute GIT-GREP-CMD on input TEST string written in temporary file. Necessary because git grep must be given a file as input, not just a string." (let ((thefile ".git.grep.test")) (dumb-jump-run-test-temp-file test thefile (concat git-grep-cmd " " thefile)))) (defun dumb-jump-run-ag-test (test ag-cmd) "Execute AG-CMD on input TEST written in temporary file. The difference is that ag ignores multiline matches when passed input from stdin, which is a crucial feature." (let ((thefile ".ag.test")) (dumb-jump-run-test-temp-file test thefile (concat ag-cmd " " thefile)))) ;; --------------------------------------------------------------------------- ;; Test dumb-jump search rules ;; --------------------------- (defun dumb-jump--search-failure-msg (tool test run-not-tests resp cmd rule) "Return search failure string adjusted to the arguments. TOOL: search tool name. TEST: the test string. RUN-NO-TESTS: t to see a list of all failed rules. RESP: response. CMD: the search command used. RULE: the complete `dumb-jump-find-rules' rule used for this test." (format "%s FAILURE '%s' %s in response '%s' | CMD: '%s' | rule: '%s'" tool test (if run-not-tests "IS unexpectedly" "NOT") resp cmd rule)) (defun dumb-jump-test-grep-rules (&optional run-not-tests) "Test all the grep rules and return count of those that fail. Optionally pass t for RUN-NOT-TESTS to see a list of all failed rules." (let ((variant (if (eq (dumb-jump-grep-installed?) 'gnu) 'gnu-grep 'grep))) (mapcan (lambda (rule) (mapcan (lambda (test) (let* ((cmd (concat "grep -En -e " (shell-quote-argument (dumb-jump-populate-regex (plist-get rule :regex) "test" variant)))) (resp (dumb-jump-run-test test cmd))) (when (or (and (not run-not-tests) (not (dumb-jump--contains-p test resp))) (and run-not-tests (> (length resp) 0))) (list (dumb-jump--search-failure-msg "grep" test run-not-tests resp cmd (plist-get rule :regex)))))) (plist-get rule (if run-not-tests :not :tests)))) (seq-filter (lambda (it) (member "grep" (plist-get it :supports))) dumb-jump-find-rules)))) (defun dumb-jump-test-ag-rules (&optional run-not-tests) "Test all the ag rules and return count of those that fail. Optionally pass t for RUN-NOT-TESTS to see a list of all failed rules." (mapcan (lambda (rule) (mapcan (lambda (test) (let* ((cmd (concat "ag --nocolor --nogroup --nonumber " (shell-quote-argument (dumb-jump-populate-regex (plist-get rule :regex) "test" 'ag)))) (resp (dumb-jump-run-ag-test test cmd))) (when (or (and (not run-not-tests) (not (dumb-jump--contains-p test resp))) (and run-not-tests (> (length resp) 0))) (list (dumb-jump--search-failure-msg "ag" test run-not-tests resp cmd rule))))) (plist-get rule (if run-not-tests :not :tests)))) (seq-filter (lambda (it) (member "ag" (plist-get it :supports))) dumb-jump-find-rules))) (defun dumb-jump-test-rg-rules (&optional run-not-tests) "Test all the rg rules and return count of those that fail. Optionally pass t for RUN-NOT-TESTS to see a list of all failed rules." (mapcan (lambda (rule) (mapcan (lambda (test) (let* ((cmd (concat "rg --color never --no-heading -U --pcre2 " (shell-quote-argument (dumb-jump-populate-regex (plist-get rule :regex) "test" 'rg)))) (resp (dumb-jump-run-test test cmd))) (when (or (and (not run-not-tests) (not (dumb-jump--contains-p test resp))) (and run-not-tests (> (length resp) 0))) (list (dumb-jump--search-failure-msg "rg" test run-not-tests resp cmd rule))))) (plist-get rule (if run-not-tests :not :tests)))) (seq-filter (lambda (it) (member "rg" (plist-get it :supports))) dumb-jump-find-rules))) (defun dumb-jump-test-git-grep-rules (&optional run-not-tests) "Test all the git grep rules and return count of those that fail. Optionally pass t for RUN-NOT-TESTS to see a list of all failed rules. Note: Tests rules that support \"grep\" since git-grep uses the same ERE syntax. Testing actual git-grep would require temp git repos." (mapcan (lambda (rule) (mapcan (lambda (test) (let* ((cmd (concat "git grep --color=never -h --untracked -E " (shell-quote-argument (dumb-jump-populate-regex (plist-get rule :regex) "test" 'git-grep)))) (resp (dumb-jump-run-git-grep-test test cmd))) (when (or (and (not run-not-tests) (not (dumb-jump--contains-p test resp))) (and run-not-tests (> (length resp) 0))) (list (dumb-jump--search-failure-msg "git-grep" test run-not-tests resp cmd rule))))) (plist-get rule (if run-not-tests :not :tests)))) (seq-filter (lambda (it) (member "grep" (plist-get it :supports))) dumb-jump-find-rules))) ;; --------------------------------------------------------------------------- (defun dumb-jump-message (str &rest args) "Log message STR with ARGS to *Messages* if not using `dumb-jump-quiet'." (unless dumb-jump-quiet (apply 'message str args)) nil) (defmacro dumb-jump-debug-message (&rest exprs) "Generate a debug message to print all expressions EXPRS." (declare (indent defun)) (let ((i 5) frames frame) ;; based on https://emacs.stackexchange.com/a/2312 (while (setq frame (backtrace-frame i)) (push frame frames) (cl-incf i)) ;; this is a macro-expanded version of the code in the stackexchange ;; code from above. This version should work on emacs-24.3, since it ;; doesn't depend on thread-last. (let* ((frame (cl-find-if (lambda (frame) (ignore-errors (and (car frame) (eq (caaddr frame) 'defalias)))) (reverse frames))) (func (cl-cadadr (cl-caddr frame))) (defun-name (symbol-name func))) (with-temp-buffer (insert "DUMB JUMP DEBUG `") (insert defun-name) (insert "` START\n----\n\n") (dolist (expr exprs) (insert (prin1-to-string expr) ":\n\t%s\n\n")) (insert "\n-----\nDUMB JUMP DEBUG `") (insert defun-name) (insert "` END\n-----") `(when dumb-jump-debug (dumb-jump-message ,(buffer-string) ,@exprs)))))) (defun dumb-jump-get-point-context (line func cur-pos) "Get the LINE context to the left and right of FUNC using CUR-POS as hint." (let ((loc (or (cl-search func line :start2 cur-pos) 0))) (list :left (substring line 0 loc) :right (substring line (+ loc (length func)))))) (defun dumb-jump-to-selected (results choices selected) "With RESULTS use CHOICES to find the SELECTED choice from multiple options." (let* ((result-index (dumb-jump--find-index (lambda (it) (string= selected it)) choices)) (result (when result-index (nth result-index results)))) (when result (dumb-jump-result-follow result)))) (defun dumb-jump-helm-persist-action (candidate) "Preview CANDIDATE in temporary buffer displaying the file at matched line. \\ This is the persistent action (\\[helm-execute-persistent-action]) for helm." (let* ((file (plist-get candidate :path)) (line (plist-get candidate :line)) (default-directory-old default-directory)) (switch-to-buffer (get-buffer-create " *helm dumb jump persistent*")) (setq default-directory default-directory-old) (fundamental-mode) (erase-buffer) (insert-file-contents file) (let ((buffer-file-name file)) (set-auto-mode) (font-lock-fontify-region (point-min) (point-max)) (goto-char (point-min)) (forward-line (1- line))))) (defun dumb-jump--format-result (proj-root result) "Return file:line:target formatted RESULT as a string stripped of PROJ-ROOT." (format "%s:%s: %s" (dumb-jump--replace proj-root "" (plist-get result :path)) (plist-get result :line) (dumb-jump--trim (plist-get result :context)))) (defun dumb-jump-ivy-jump-to-selected (results choices _proj) "Offer CHOICES as candidates through `ivy-read'. Then execute dumb-jump-result-follow' on the selected choice RESULTS. Ignore _PROJ." (if (fboundp 'ivy-read) (ivy-read "Jump to: " (dumb-jump--zip-pair choices results) :action (lambda (cand) (dumb-jump-result-follow (cdr cand))) :caller 'dumb-jump-ivy-jump-to-selected) (error "ivy-read is unknown. Is it loaded?"))) (defun dumb-jump-prompt-user-for-choice (proj-root results) "Prompt user to select one of the identified RESULTS in PROJ-ROOT. Strip the PROJ-ROOT (project root path) from the file name shown in each entry. Prompt with the mechanism selected by `dumb-jump-selector' user-option if available otherwise default to `completing-read'." (let ((choices (mapcar (lambda (it) (dumb-jump--format-result proj-root it)) results))) (cond ((eq dumb-jump-selector 'completing-read) (dumb-jump-to-selected results choices (dumb-jump--select-choice choices "Jump to: "))) ;; ((and (eq dumb-jump-selector 'ivy) (fboundp 'ivy-read)) (funcall dumb-jump-ivy-jump-to-selected-function results choices proj-root)) ;; ((and (eq dumb-jump-selector 'helm) (fboundp 'helm)) (helm :sources (with-no-warnings (helm-make-source "Jump to: " 'helm-source-sync :action '(("Jump to match" . dumb-jump-result-follow)) :candidates (dumb-jump--zip-pair choices results) :persistent-action 'dumb-jump-helm-persist-action)) :buffer "*helm dumb jump choices*")) ;; fallback for unsupported selector values (t (dumb-jump-to-selected results choices (dumb-jump--select-choice choices "Jump to: ")))))) (defun dumb-jump-get-project-root (filepath) "Return project root holding FILEPATH. Keep looking at the parent dir of FILEPATH until a denoter file/dir is found. Fall back to `project.el' when available and no denoter is found. Return a directory name without the trailing slash." (let* ((path (expand-file-name filepath)) (dir (if (file-directory-p path) (file-name-as-directory path) (file-name-directory path))) (project-el-root (when (and (null dumb-jump-project) dir (fboundp 'project-current)) (let ((default-directory dir)) (let ((proj (ignore-errors (project-current nil)))) (when proj (if (fboundp 'project-root) (project-root proj) (with-no-warnings ;; On Emacs 26.1 use now obsolete `project-roots' (car (project-roots proj)))))))))) (directory-file-name (expand-file-name (or dumb-jump-project (locate-dominating-file path #'dumb-jump-get-config) project-el-root dumb-jump-default-project))))) (defvar dumb-jump--rust-deps-cache (make-hash-table :test 'equal) "Cache of Rust dependency paths keyed by project root.") (defun dumb-jump-get-rust-dependency-paths (proj-root) "Return list of source directories for Rust dependencies in PROJ-ROOT. Uses `cargo metadata' to discover dependency crate source paths. Results are cached per project root." (let ((cached (gethash proj-root dumb-jump--rust-deps-cache 'dumb-jump--no-cache))) (if (not (eq cached 'dumb-jump--no-cache)) cached (let ((dep-paths (when (file-exists-p (expand-file-name "Cargo.toml" proj-root)) (let* ((default-directory proj-root) (json-output (with-temp-buffer (let ((exit-code (call-process "cargo" nil t nil "metadata" "--format-version=1"))) (when (eq exit-code 0) (buffer-string))))) (json-data (ignore-errors (when json-output (json-read-from-string json-output)))) (packages (when json-data (cdr (assoc 'packages json-data)))) (paths (when packages (seq-uniq (seq-filter #'file-directory-p (mapcar (lambda (pkg) (file-name-directory (cdr (assoc 'manifest_path pkg)))) packages)))))) (seq-remove (lambda (p) (string= (directory-file-name p) (directory-file-name proj-root))) paths))))) (puthash proj-root dep-paths dumb-jump--rust-deps-cache) dep-paths)))) (defun dumb-jump-get-config (dir) "If a project denoter is in DIR then return it, otherwise nil. However, if DIR contains a `.dumbjumpignore' it returns nil to keep looking for another root." (if (file-exists-p (expand-file-name ".dumbjumpignore" dir)) nil (car (seq-filter (lambda (it) (file-exists-p (expand-file-name it dir))) dumb-jump-project-denoters)))) (defun dumb-jump-get-language (file) "Get language from FILE extension. Fallback to using `major-mode' name." (let* ((language (or (dumb-jump-get-language-from-mode) (dumb-jump-get-language-by-filename file) (dumb-jump-get-mode-base-name)))) ; src edit buffer ? org-edit-src-exit (if (and (fboundp 'org-src-edit-buffer-p) (org-src-edit-buffer-p)) (progn (setq language "org") (with-no-warnings (org-edit-src-exit)) (if (and (boundp 'org-version) (version< org-version "9")) (save-buffer)))) (if (string= language "org") (setq language (dumb-jump-get-language-in-org))) (if (member language (seq-uniq (mapcar (lambda (it) (plist-get it :language)) dumb-jump-find-rules))) language (format ".%s file" (or (file-name-extension file) ""))))) (defun dumb-jump-get-language-in-org () "Return associated language in org mode src block or \"org\" if outside src. This assumes point is inside an org mode buffer." (let ((lang (nth 0 (with-no-warnings (org-babel-get-src-block-info))))) ;; if lang exists then create a composite language (if lang (dumb-jump-make-composite-language "org" (if (dumb-jump-get-language-from-aliases lang) (dumb-jump-get-language-from-aliases lang) lang) "org" "org" "org") "org"))) (defun dumb-jump-add-language-to-proplist (newlang proplist lang) "Return nil if NEWLANG is already in PROPLIST or (if not) return a new proplist. The new proplis is PROPLIS where a NEWLANG plist(s) is (are) added to PROPLIST. The plist(s) value of NEWLANG is (are) copied from those of LANG and LANG is replaced by NEWLANG." (unless (seq-filter (lambda (it) (string= newlang (plist-get it :language))) proplist) (dumb-jump--splice (lambda (it) (string= lang (plist-get it :language))) (lambda (it) (list it (plist-put (copy-tree it) :language newlang))) proplist))) (defun dumb-jump-make-composite-language (mode lang extension agtype rgtype) "Concat one MODE (usually the string org) with a LANG (c or python or etc) to make a composite language of the form cPLUSorg or pythonPLUSorg or etc. Modify `dumb-jump-find-rules' and `dumb-jump-language-file-exts' accordingly \(using EXTENSION AGTYPE RGTYPE)" (let* ((complang (concat lang "PLUS" mode)) (alreadyextension (seq-filter (lambda (it) (and (string= complang (plist-get it :language)) (string= extension (plist-get it :ext)) (string= agtype (plist-get it :agtype)) (string= rgtype (plist-get it :rgtype)))) dumb-jump-language-file-exts)) (newfindrule (dumb-jump-add-language-to-proplist complang dumb-jump-find-rules lang)) (newfileexts (dumb-jump-add-language-to-proplist complang dumb-jump-language-file-exts lang))) (when newfindrule (set-default 'dumb-jump-find-rules newfindrule)) (unless dumb-jump-search-type-org-only-org (when newfileexts (set-default 'dumb-jump-language-file-exts newfileexts))) (unless alreadyextension (set-default 'dumb-jump-language-file-exts (cons `(:language ,complang :ext ,extension :agtype ,agtype :rgtype ,rgtype) dumb-jump-language-file-exts))) complang)) (defcustom dumb-jump-language-aliases-alist '(("sh" . "shell") ("d" . "dlang") ("cperl" . "perl") ("octave" . "matlab") ("emacs-lisp" . "elisp") ("R" . "r") ("arduino" . "c++")) "An alist of language alias to language name. Aliases are major mode base name sometimes used for some files. For example: \"cperl\" for the \"perl\" language." :group 'dumb-jump :type '(repeat (cons (string :tag "alias ") (string :tag "language")))) (defun dumb-jump-get-language-from-aliases (lang) "Return the dumb-jump language name for a possible alias if there is one. Return nil if LANG is not one of the supported aliases." (assoc-default lang dumb-jump-language-aliases-alist)) (defun dumb-jump-get-mode-base-name () "Get the base name of the mode." (dumb-jump--replace "-mode" "" (symbol-name major-mode))) (defun dumb-jump-get-language-from-mode () "Extract the language from the `major-mode' name. Currently just everything before \"-mode\"." (dumb-jump-get-language-from-aliases (dumb-jump-get-mode-base-name))) (defun dumb-jump-get-language-by-filename (file) "Get the programming language name from the specified FILE extension." ;; Search in `dumb-jump-language-file-exts' for an entry that has the :ext ;; attribute equal to the file extension and return the corresponding ;; :language value. (let* ((filename (if (string-suffix-p ".gz" file) (file-name-sans-extension file) file)) (result (seq-filter (lambda (it) (string-suffix-p (concat "." (plist-get it :ext)) filename)) dumb-jump-language-file-exts))) (when (and result (eq (length result) 1)) (plist-get (car result) :language)))) ;; -- (defun dumb-jump-issue-result (issue) "Return a result property list with the ISSUE set as :issue property symbol." `(:results nil :lang nil :symbol nil :ctx-type nil :file nil :root nil :issue ,(intern issue))) (defun dumb-jump-get-results (&optional entered-name) "Perform the dumb-jump search for text at point, region or ENTERED-NAME. Runs `dumb-jump-fetch-results' under conditions adjusted to the current context. Perform the search if searcher is installed, buffer is saved, and there's a symbol under point or an item to search is specified by the ENTERED-NAME string. Return the dumb-jump alist result: - :results - :lang : string: language name - :symbol : string: symbol being searched - :ctx-type: string: - :file : string: file name - :root : string: project root - :issue : symbol: problem description symbol: nogrep or nosymbol." ;; Prevent a empty string entered-name from being interpreted as user entry ;; because `read-from-minibuffer' may return "". (when (and entered-name (string-blank-p entered-name)) (setq entered-name nil)) (cond ;; When no search tool is available just return an empty result identifying ;; a nogrep issue. ((not (or (dumb-jump-ag-installed?) (dumb-jump-rg-installed?) (dumb-jump-git-grep-installed?) (dumb-jump-grep-installed?))) (dumb-jump-issue-result "nogrep")) ;; Inside a shell or eshell buffer, ((derived-mode-p 'shell-mode 'eshell-mode) (dumb-jump-fetch-shell-results entered-name)) ;; When inside a normal buffer but no string specified by ENTERED-NAME, ;; point or region does not identify something to search, ;; just return an empty result identifying a nosymbol issue. ((and (not entered-name) (not (region-active-p)) (not (thing-at-point 'symbol))) (dumb-jump-issue-result "nosymbol")) ;; Otherwise, perform the search for the text identified by string specified ;; by ENTERED-NAME, point or region. (t (dumb-jump-fetch-file-results entered-name)))) (defun dumb-jump-fetch-shell-results (&optional entered-name) "Perform search from shell buffer for text at point, region or ENTERED-NAME. Return the dumb-jump alist result: - :results - :lang : string: language name - :symbol : string: symbol being searched - :ctx-type: string: - :file : string: file name - :root : string: project root." (let* ((cur-file (buffer-name)) (proj-root (dumb-jump-get-project-root default-directory)) (proj-config (dumb-jump-get-config proj-root)) (config (when (string-suffix-p ".dumbjump" proj-config) (dumb-jump-read-config proj-root proj-config))) (lang (or (plist-get config :language) (car (dumb-jump-get-lang-by-shell-contents (buffer-name)))))) (dumb-jump-fetch-results cur-file proj-root lang config entered-name))) (defun dumb-jump-fetch-file-results (&optional entered-name) "Perform a file search for text at point, region or ENTERED-NAME string. Return the dumb-jump alist result: - :results - :lang : string: language name - :symbol : string: symbol being searched - :ctx-type: string: - :file : string: file name - :root : string: project root." (let* ((cur-file (or (buffer-file-name) "")) (proj-root (dumb-jump-get-project-root cur-file)) (proj-config (dumb-jump-get-config proj-root)) (config (when (string-suffix-p ".dumbjump" proj-config) (dumb-jump-read-config proj-root proj-config))) (lang (or (plist-get config :language) (dumb-jump-get-language cur-file)))) (dumb-jump-fetch-results cur-file proj-root lang config entered-name))) ;; -- (defun dumb-jump-process-symbol-by-lang (lang look-for) "Process and potentially transform LANG's LOOK-FOR. For instance, remove clojure namespace prefix." (cond ((and (string= lang "commonlisp") (dumb-jump--contains-p ":" look-for) (not (string-prefix-p ":" look-for))) (nth 1 (dumb-jump--split ":" look-for 'omit-nulls))) ((and (string= lang "clojure") (dumb-jump--contains-p "/" look-for)) (nth 1 (dumb-jump--split "/" look-for))) ((and (string= lang "fennel") (dumb-jump--contains-p "." look-for)) (car (last (dumb-jump--split "\\." look-for)))) ((and (string= lang "ruby") (dumb-jump--contains-p "::" look-for)) (car (last (dumb-jump--split "::" look-for)))) ((and (or (string= lang "ruby") (string= lang "crystal")) (string-prefix-p ":" look-for)) (dumb-jump--chop-prefix ":" look-for)) ((and (string= lang "systemverilog") (string-prefix-p "`" look-for)) (dumb-jump--chop-prefix "`" look-for)) (t look-for))) (defun dumb-jump-get-point-line () "Get line at point." (thing-at-point 'line t)) (defun dumb-jump-get-point-symbol () "Get symbol at point." (if (region-active-p) (buffer-substring-no-properties (region-beginning) (region-end)) (thing-at-point 'symbol t))) (defun dumb-jump--get-symbol-start () "Get the start of symbol at point." (- (if (region-active-p) (region-beginning) (car (bounds-of-thing-at-point 'symbol))) (line-beginning-position))) (defun dumb-jump-get-lang-by-shell-contents (buffer) "Return languages in BUFFER by checking if file extension is mentioned." (let* ((buffer-contents (with-current-buffer buffer (buffer-string))) (found (seq-filter (lambda (it) (dumb-jump--match (concat "\\." (regexp-quote (plist-get it :ext)) "\\b") buffer-contents)) dumb-jump-language-file-exts))) (mapcar (lambda (it) (plist-get it :language)) found))) (defun dumb-jump-fetch-results (cur-file proj-root lang config &optional entered-name) "Search for symbol in ENTERED-NAME or symbol at point. Return a list of results based on current file context and calling the currently selected searcher tool (grep, ag, rg, ...). CUR-FILE is the path of the current buffer. PROJ-ROOT is that file's root project directory. LANG is a string programming language with CONFIG a property list of project configuration. ENTERED-NAME is an optional string identifying item to search. The returned property list has the following members: - :results: - :lang : string: language name - :symbol : string: symbol being searched - :ctx-type: string: - :file : string: file name - :root : string: project root." (let* ((cur-line-num (line-number-at-pos)) (found-symbol (or entered-name (dumb-jump-get-point-symbol))) (look-for (dumb-jump-process-symbol-by-lang lang found-symbol)) (pt-ctx (if entered-name (get-text-property 0 :dumb-jump-ctx entered-name) (dumb-jump-get-point-context (dumb-jump-get-point-line) look-for (dumb-jump--get-symbol-start)))) (ctx-type (unless (eq dumb-jump--search-mode 'references) (dumb-jump-get-ctx-type-by-language lang pt-ctx))) (gen-funcs (dumb-jump-pick-grep-variant proj-root)) (parse-fn (plist-get gen-funcs :parse)) ; dumb-jump-parse-TOOL-response (generate-fn (plist-get gen-funcs :generate)) (searcher (plist-get gen-funcs :searcher)) (regexes (if (eq dumb-jump--search-mode 'references) ;; Broad symbol search with a left boundary that also ;; works for Lisp-family identifiers like *foo*. (list "(^|[^a-zA-Z0-9\\?\\*-])JJJ\\j") (dumb-jump-get-contextual-regexes lang ctx-type searcher))) ;; If selected searcher has no rules for this language, try alternatives. ;; Only block fallback when dumb-jump-force-searcher is a direct ;; searcher symbol. When it's a function or directory list that ;; didn't match this project, the auto-selected searcher should ;; still be eligible for fallback. (_alt-searcher (when (and (null regexes) (not (eq dumb-jump--search-mode 'references)) (not (memq dumb-jump-force-searcher '(ag rg grep gnu-grep git-grep git-grep-plus-ag)))) (let ((alt (seq-find (lambda (s) (and (not (eq s searcher)) (if (eq s 'gnu-grep) (eq (dumb-jump-grep-installed?) 'gnu) (funcall (plist-get (dumb-jump-generators-by-searcher s) :installed))) (dumb-jump-get-rules-by-language lang s))) '(ag rg git-grep gnu-grep grep)))) (when alt (setq gen-funcs (dumb-jump-generators-by-searcher alt)) (setq parse-fn (plist-get gen-funcs :parse)) (setq generate-fn (plist-get gen-funcs :generate)) (setq searcher (plist-get gen-funcs :searcher)) (setq regexes (dumb-jump-get-contextual-regexes lang ctx-type searcher)))))) (exclude-paths (when config (plist-get config :exclude))) (include-paths (when config (plist-get config :include))) (rust-dep-paths (when (and (string= lang "rust") dumb-jump-rust-search-dependencies) (dumb-jump-get-rust-dependency-paths proj-root))) (extra-paths (when dumb-jump-extra-search-paths-function (let* ((hook-lang (if (and (stringp lang) (string-match "\\`\\(.+\\)PLUS" lang)) (match-string 1 lang) lang)) (paths (funcall dumb-jump-extra-search-paths-function hook-lang proj-root))) (when (listp paths) (mapcan (lambda (p) (when (stringp p) (let ((expanded (expand-file-name p proj-root))) (if (file-directory-p expanded) (list expanded) (when dumb-jump-debug (dumb-jump-message "Dumb Jump: extra-search-paths dropping %S (not a directory)" expanded)) nil)))) paths))))) ;; we will search proj root and all include paths (search-paths (seq-uniq (append (list proj-root) include-paths (or rust-dep-paths '()) (or extra-paths '())))) (_search-paths-debug (when dumb-jump-debug (dumb-jump-message "Dumb Jump: search-paths = %S" search-paths))) (raw-results (mapcan (lambda (it) (dumb-jump-run-command look-for it regexes lang exclude-paths cur-file cur-line-num parse-fn generate-fn)) search-paths)) (results (let ((deduped (delete-dups (mapcar (lambda (it) (plist-put it :target look-for)) raw-results)))) (if (eq dumb-jump--search-mode 'references) (dumb-jump-filter-definition-results deduped lang look-for searcher) deduped)))) `(:results ,results :lang ,(if (null lang) "" lang) :symbol ,look-for :ctx-type ,(if (null ctx-type) "" ctx-type) :file ,cur-file :root ,proj-root))) ;;;###autoload (defun dumb-jump-back () "Jump back to where the last jump was done." ;; Note: made obsolete by Emacs `xref-pop-marker-stack'. (interactive) (with-demoted-errors "Error running `dumb-jump-before-jump-hook': %S" (run-hooks 'dumb-jump-before-jump-hook)) (pop-tag-mark) (with-demoted-errors "Error running `dumb-jump-after-jump-hook': %S" (run-hooks 'dumb-jump-after-jump-hook))) ;;;###autoload (defun dumb-jump-quick-look () "Run `dumb-jump-go' in quick look mode. That is, show a tooltip of where it would jump instead." ;; Note: independent of the Emacs xref interface. (interactive) (with-no-warnings (dumb-jump-go t))) ;;;###autoload (defun dumb-jump-go-other-window () "Like dumb-jump-go' but use `find-file-other-window' instead of `find-file'." ;; Note: independent of the Emacs xref interface. (interactive) (let ((dumb-jump-window 'other)) (with-no-warnings (dumb-jump-go)))) ;;;###autoload (defun dumb-jump-go-current-window () "Like `dumb-jump-go' but always use `find-file'." ;; Note: independent of the Emacs xref interface. (interactive) (let ((dumb-jump-window 'current)) (with-no-warnings (dumb-jump-go)))) ;;;###autoload (defun dumb-jump-go-prefer-external () "Like `dumb-jump-go' but prefer external matches from the current file." ;; Note: independent of the Emacs xref interface. (interactive) (with-no-warnings (dumb-jump-go nil t))) ;;;###autoload (defun dumb-jump-find-references () "Find references/usages of the symbol at point. This is the inverse of `dumb-jump-go' -- it finds where a symbol is used rather than where it is defined." ;; Note: independent of the Emacs xref interface. (interactive) (let ((dumb-jump--search-mode 'references) (dumb-jump-aggressive nil)) (with-no-warnings (dumb-jump-go)))) ;;;###autoload (defun dumb-jump-go-prompt () "Like `dumb-jump-go' but prompt for function instead of using under point." ;; Note: independent of the Emacs xref interface. (interactive) (with-no-warnings (dumb-jump-go nil nil (read-from-minibuffer "Jump to: ")))) ;;;###autoload (defun dumb-jump-go-prefer-external-other-window () "Like `dumb-jump-go-prefer-external' but create another window. It uses `find-file-other-window' instead of `find-file'." ;; Note: independent of the Emacs xref interface. (interactive) (let ((dumb-jump-window 'other)) (with-no-warnings (dumb-jump-go-prefer-external)))) ;;;###autoload (defun dumb-jump-go (&optional use-tooltip prefer-external entered-name) "Go to the function/variable declaration for thing at point. When USE-TOOLTIP is t a tooltip jump preview will show instead. When PREFER-EXTERNAL is t it will sort external matches before current file. The optional ENTERED-NAME string is the name of the item to jump to manually selected by user as response to a prompt." ;; Note: independent of the Emacs xref interface. (interactive "P") (let* ((start-time (float-time)) (info (dumb-jump-get-results entered-name)) (end-time (float-time)) (fetch-time (- end-time start-time)) (results (plist-get info :results)) (look-for (or entered-name (plist-get info :symbol))) (proj-root (plist-get info :root)) (issue (plist-get info :issue)) (lang (plist-get info :lang)) (result-count (length results))) (when (> fetch-time dumb-jump-max-find-time) (dumb-jump-message "Took over %ss to find '%s'. \ Please install ag or rg, or add a .dumbjump file to '%s' with path exclusions" (number-to-string dumb-jump-max-find-time) look-for proj-root)) (cond ((eq issue 'nogrep) (dumb-jump-message "Please install ag, rg, git grep or grep!")) ((eq issue 'nosymbol) (dumb-jump-message "No symbol under point.")) ((string-suffix-p " file" lang) (dumb-jump-message "Could not find rules for '%s'." lang)) ((= result-count 1) (dumb-jump-result-follow (car results) use-tooltip proj-root)) ((> result-count 1) ;; multiple results so let the user pick from a list ;; unless the match is in the current file (dumb-jump-handle-results results (plist-get info :file) proj-root (plist-get info :ctx-type) look-for use-tooltip prefer-external lang)) ((= result-count 0) (dumb-jump-message "'%s' %s %s %s not found." look-for (if (string-blank-p (or lang "")) "with unknown language so" lang) (plist-get info :ctx-type) (if (eq dumb-jump--search-mode 'references) "references" "declaration")))))) (defcustom dumb-jump-language-comments '((:comment "//" :language "c++") (:comment "/*" :language "c++") (:comment ";" :language "elisp") (:comment ";" :language "commonlisp") (:comment "//" :language "javascript") (:comment "//" :language "typescript") (:comment "//" :language "dart") (:comment "//" :language "dlang") (:comment "/*" :language "dlang") (:comment "/+" :language "dlang") (:comment "--" :language "haskell") (:comment "--" :language "lua") (:comment "//" :language "rust") (:comment "#" :language "julia") (:comment "//" :language "objc") (:comment "//" :language "csharp") (:comment "//" :language "java") (:comment ";" :language "clojure") (:comment "#" :language "coffeescript") (:comment "//" :language "faust") (:comment ";" :language "fennel") (:comment "!" :language "fortran") (:comment "//" :language "go") (:comment "//" :language "zig") (:comment "#" :language "perl") (:comment "#" :language "tcl") (:comment "//" :language "php") (:comment "#" :language "php") (:comment "--" :language "purescript") (:comment "#" :language "python") (:comment "%" :language "matlab") (:comment "#" :language "r") (:comment ";" :language "racket") (:comment "#" :language "ruby") (:comment "#" :language "crystal") (:comment "#" :language "nim") (:comment "#" :language "nix") (:comment "//" :language "scala") (:comment ";" :language "scheme") (:comment "#" :language "janet") (:comment "#" :language "shell") (:comment "//" :language "solidity") (:comment "//" :language "swift") (:comment "#" :language "elixir") (:comment "%" :language "erlang") (:comment "%" :language "tex") (:comment "//" :language "systemverilog") (:comment "--" :language "vhdl") (:comment "//" :language "scss") (:comment "//" :language "pascal") (:comment "//" :language "protobuf") (:comment "#" :language "hcl") (:comment "//" :language "apex") (:comment "//" :language "odin") (:comment "/*" :language "odin") (:comment "*>" :language "cobol")) "List of one-line comments organized by language. A language may have more than 1 comment string." :group 'dumb-jump :type '(repeat (plist :options ((:comment string) (:language string))))) (defun dumb-jump-get-comment-by-language (lang) "Return a list of the one-line comment or comments for the given LANG." (let* ((entries (seq-uniq (seq-filter (lambda (it) (string= (plist-get it :language) lang)) dumb-jump-language-comments)))) (if (> (length entries) 0) (mapcar (lambda (pl) (plist-get pl :comment)) entries) nil))) (defun dumb-jump--context-startswith-comment (context-string comments) "Return t when CONTEXT-STRING starts with any COMMENTS string." (let ((startswith-comment nil) ;; trim leading white-space from context-string (context-str (replace-regexp-in-string "^\\s-+" "" context-string))) (catch 'found-one (dolist (comment comments) (when (string-prefix-p comment context-str) (setq startswith-comment t) (throw 'found-one nil)))) startswith-comment)) (defun dumb-jump-filter-no-start-comments (results lang) "Filter out RESULTS with a :context starting with a LANG comment in file." (let ((comments (dumb-jump-get-comment-by-language lang))) (if comments (seq-filter (lambda (it) (not (dumb-jump--context-startswith-comment (plist-get it :context) comments))) results) results))) (defun dumb-jump-handle-results (results cur-file proj-root ctx-type look-for use-tooltip prefer-external &optional language) "Handle the searchers results. RESULTS is a list of property lists with the searcher's results. CUR-FILE is the current file within PROJ-ROOT. CTX-TYPE is a string of the current context. LOOK-FOR is the symbol we're jumping for. USE-TOOLTIP shows a preview instead of jumping. PREFER-EXTERNAL will sort current file last. LANGUAGE is an optional language to pass to `dumb-jump-process-results'." (let* ((processed (dumb-jump-process-results results cur-file proj-root ctx-type look-for use-tooltip prefer-external language)) (results (plist-get processed :results)) (do-var-jump (plist-get processed :do-var-jump)) (var-to-jump (plist-get processed :var-to-jump)) (match-cur-file-front (plist-get processed :match-cur-file-front))) (dumb-jump-debug-message look-for ctx-type var-to-jump (pp-to-string match-cur-file-front) (pp-to-string results) prefer-external proj-root cur-file) (cond (use-tooltip ;; quick-look mode (let* ((choices (mapcar (lambda (it) (dumb-jump--format-result proj-root it)) results)) (selected-choice (dumb-jump--select-choice choices "Matches: ")) (selected-result (cdr (assoc selected-choice (dumb-jump--zip-pair choices results))))) (when selected-result (dumb-jump--show-preview (dumb-jump--format-result proj-root selected-result))))) (do-var-jump (dumb-jump-result-follow var-to-jump use-tooltip proj-root)) (t (dumb-jump-prompt-user-for-choice proj-root match-cur-file-front))))) (defun dumb-jump--candidate-x (plist-get it :diff) 0) (or (string= (plist-get it :path) cur-file) (string= (plist-get it :path) rel-cur-file)))) match-no-comments) (seq-filter (lambda (it) (and (<= (plist-get it :diff) 0) (or (string= (plist-get it :path) cur-file) (string= (plist-get it :path) rel-cur-file)))) match-no-comments) (seq-sort #'dumb-jump--candidate-x (length line-num-raw) 1) (nth 1 line-num-raw)))) (cond ;; fixes rare bug where context is blank but file is defined "/somepath/file.txt:14:" ;; OR: (and (= (length parts) 1) (file-name-exists (nth 0 parts))) ((dumb-jump--match ":[0-9]+:$" resp-line) nil) ((and parts line-num) (if (= (length parts) 2) (list (let ((path (expand-file-name (nth 0 parts)))) (if (file-name-absolute-p (nth 0 parts)) path (file-relative-name path))) line-num (nth 1 parts)) ; this case is when they are searching a particular file... (list (let ((path (expand-file-name cur-file))) (if (file-name-absolute-p cur-file) path (file-relative-name path))) line-num (nth 0 parts))))))) ;; -- ;; Parse Search Tool Results: indirectly called by `dumb-jump-run-command' ;; ----------------------------------------------------------------------- ;; - `dumb-jump-parse-grep-response' ;; - `dumb-jump-parse-ag-response' ;; - `dumb-jump-parse-rg-response' ;; - `dumb-jump-parse-git-grep-response' ;; - `dumb-jump-parse-response-lines' (defun dumb-jump-parse-response-lines (parsed cur-file cur-line-num) "Turn PARSED response lines into a list of property lists. Using CUR-FILE and CUR-LINE-NUM to exclude jump origin." (let* ((records (mapcan (lambda (it) (when it (let* ((line-num (string-to-number (nth 1 it))) (diff (- cur-line-num line-num))) (list `(:path ,(nth 0 it) :line ,line-num :context ,(nth 2 it) :diff ,diff))))) parsed)) (results (delq nil records))) (seq-filter (lambda (it) (not (and (string= (plist-get it :path) cur-file) (= (plist-get it :line) cur-line-num)))) results))) (defun dumb-jump-parse-grep-response (resp cur-file cur-line-num) "Parse raw grep search response RESP into a list of plists. - CUR-FILE: current file. - CUR-LINE-NUM: current line number." (let* ((resp-no-warnings (seq-filter (lambda (it) (and (not (string-prefix-p "grep:" it)) (not (dumb-jump--contains-p "No such file or" it)))) (dumb-jump--split "\n" (dumb-jump--trim resp)))) (parsed (mapcar (lambda (it) (dumb-jump-parse-response-line it cur-file)) resp-no-warnings))) (dumb-jump-parse-response-lines parsed cur-file cur-line-num))) (defun dumb-jump--clean-wsl-response (resp) "Clean up RESP for Windows WSL/UNC path issues. Filters CMD.exe UNC path warnings and fixes /wsl$/ to //wsl$/." (let* ((cleaned (replace-regexp-in-string "CMD\\.EXE was started with the above path as the current directory\\.\nUNC paths are not supported\\..*?\n" "" resp)) (fixed (replace-regexp-in-string "^/wsl\\$/" "//wsl$/" cleaned))) (replace-regexp-in-string "\n/wsl\\$/" "\n//wsl$/" fixed))) (defun dumb-jump-parse-ag-response (resp cur-file cur-line-num) "Parse raw ag search response RESP into a list of plists. - CUR-FILE: current file. - CUR-LINE-NUM: current line number." (let* ((cleaned (dumb-jump--clean-wsl-response resp)) (resp-lines (dumb-jump--split "\n" (dumb-jump--trim cleaned))) (parsed (mapcar (lambda (it) (dumb-jump-parse-response-line it cur-file)) resp-lines))) (dumb-jump-parse-response-lines parsed cur-file cur-line-num))) (defun dumb-jump-parse-rg-response (resp cur-file cur-line-num) "Parse raw rg search response RESP into a list of plists. - CUR-FILE: current file. - CUR-LINE-NUM: current line number." (let* ((resp-lines (dumb-jump--split "\n" (dumb-jump--trim resp))) (parsed (mapcar (lambda (it) (dumb-jump-parse-response-line it cur-file)) resp-lines))) (dumb-jump-parse-response-lines parsed cur-file cur-line-num))) (defun dumb-jump-parse-git-grep-response (resp cur-file cur-line-num) "Parse raw git grep response RESP into a list of plists. - CUR-FILE: current file. - CUR-LINE-NUM: current line number." (let* ((resp-lines (dumb-jump--split "\n" (dumb-jump--trim resp))) (parsed (mapcar (lambda (it) (dumb-jump-parse-response-line it cur-file)) resp-lines))) (dumb-jump-parse-response-lines parsed cur-file cur-line-num))) ;; -- (defun dumb-jump-re-match (re s) "Does regular expression RE match string S. If RE is nil return nil." (when (and re s) (dumb-jump--match re s))) (defun dumb-jump-get-ctx-type-by-language (lang pt-ctx) "Detect the type of context by the language LANG and its context PT-CTX. PT-CTX is assumed to be a property list with the :left and :right attributes that are both strings." (let* ((contexts (seq-filter (lambda (it) (string= (plist-get it ':language) lang)) dumb-jump-language-contexts)) (usable-ctxs (when (> (length contexts) 0) (seq-filter (lambda (it) (and (or (null (plist-get it :left)) (dumb-jump-re-match (plist-get it :left) (plist-get pt-ctx :left))) (or (null (plist-get it :right)) (dumb-jump-re-match (plist-get it :right) (plist-get pt-ctx :right))))) contexts))) (use-ctx (= (length (seq-filter (lambda (it) (string= (plist-get it ':type) (and usable-ctxs (plist-get (car usable-ctxs) :type)))) usable-ctxs)) (length usable-ctxs)))) (when (and usable-ctxs use-ctx) (plist-get (car usable-ctxs) :type)))) (defun dumb-jump-get-ext-includes (language) "Return the --include grep argument of file extensions for LANGUAGE." (let ((exts (dumb-jump-get-file-exts-by-language language))) (dumb-jump-arg-joiner "--include" (mapcar (lambda (it) (format "\\*.%s" it)) exts)))) (defun dumb-jump-arg-joiner (prefix values) "Helper to generate command arg with its PREFIX for each value in VALUES." (let* ((normalized-values (mapcar (lambda (it) (dumb-jump--trim (or it ""))) values)) (non-empty-values (seq-filter (lambda (it) (> (length it) 0)) normalized-values)) (args (dumb-jump--join (format " %s " prefix) non-empty-values))) (if non-empty-values (format " %s %s " prefix args) ""))) (defun dumb-jump-get-contextual-regexes (lang ctx-type searcher) "Get list of search regular expressions by LANG and CTX-TYPE. The arguments are: - LANG: string: the language. - CTX-TYPE: variable, function, etc. - SEARCHER: symbol: the name of the search tool. One of: git-grep, ag, git-grep-plus-ag, rg, gnu-grep, grep." (let* ((raw-rules (dumb-jump-get-rules-by-language lang searcher)) (ctx-type (unless dumb-jump-ignore-context ctx-type)) (ctx-rules (if ctx-type (seq-filter (lambda (it) (string= (plist-get it :type) ctx-type)) raw-rules) raw-rules)) (rules (or ctx-rules raw-rules)) (regexes (mapcar (lambda (it) (plist-get it :regex)) rules))) regexes)) (defun dumb-jump-populate-regex (it look-for variant) "Populate IT regex template with LOOK-FOR for the specific search VARIANT. Replace the dumb-jump generic regular expression meta concepts with the one required for the search variant tool. The arguments are: - IT : string: the dumb-jump generic regular expression. - LOOK-FOR: string: the searched item. - VARIANT : symbol: the search tool variant, one of: ag, rg, grep, gnu-grep, git-grep-plus-ag, git-grep." (let ((boundary (cond ((eq variant 'rg) dumb-jump-rg-word-boundary) ((eq variant 'ag) dumb-jump-ag-word-boundary) ((eq variant 'git-grep-plus-ag) dumb-jump-ag-word-boundary) ((eq variant 'git-grep) dumb-jump-git-grep-word-boundary) (t dumb-jump-grep-word-boundary)))) (let ((text it)) (setq text (dumb-jump--replace "\\j" boundary text)) (when (dumb-jump-use-space-bracket-exp-for variant) (setq text (dumb-jump--replace "\\s" "[[:space:]]" text))) (setq text (dumb-jump--replace "JJJ" (regexp-quote look-for) text)) text))) (defun dumb-jump-populate-regexes (look-for regexes variant) "Return list of VARIANT specific regexes from the dumb-jump generic REGEXES. Update the generic regexes, populate the target identified in LOOK-FOR. The arguments are: - LOOK-FOR: string: the searched item. - REGEXES: list of strings: each string is a dumb-jump generic regular expression. - VARIANT: symbol: the search tool variant, one of: ag, rg, git-grep-plus-ag, git-grep." (mapcar (lambda (it) (dumb-jump-populate-regex it look-for variant)) regexes)) (defun dumb-jump-pcre-to-emacs (regex) "Convert a PCRE-style REGEX string to Emacs regex syntax. In PCRE and Emacs, +, ?, *, ., ^, $, [ are special without escaping. But (, ), |, {, } differ: special unescaped in PCRE, special escaped in Emacs. This function swaps the literal/special meaning of (, ), |, {, and }." (let ((i 0) (len (length regex)) (out (list))) (while (< i len) (let ((ch (aref regex i))) (cond ;; Backslash sequence ((and (= ch ?\\) (< (1+ i) len)) (let ((next (aref regex (1+ i)))) (cond ((memq next '(?\( ?\) ?| ?{ ?})) ;; \X (literal in PCRE) → X (literal in Emacs) (push (char-to-string next) out) (setq i (+ i 2))) (t ;; Other backslash sequences (\b, \w, \+, \?, etc.) pass through (push (substring regex i (+ i 2)) out) (setq i (+ i 2)))))) ;; (? prefixed groups: (?:, (?!, (?=, (?<=, (? (length it) 0)) normalized-parts))) (dumb-jump--join " " non-empty-parts))) (defun dumb-jump-get-file-exts-by-language (language) "Return list of file extensions for a LANGUAGE." (mapcar (lambda (it) (plist-get it :ext)) (seq-filter (lambda (it) (string= (plist-get it :language) language)) dumb-jump-language-file-exts))) (defun dumb-jump-get-ag-type-by-language (language) "Return list of ag type argument for a LANGUAGE." (seq-uniq (mapcar (lambda (it) (plist-get it :agtype)) (seq-filter (lambda (it) (and (plist-get it :agtype) (string= (plist-get it :language) language))) dumb-jump-language-file-exts)))) (defun dumb-jump-get-rg-type-by-language (language) "Return list of rg type argument for a LANGUAGE." (seq-uniq (mapcar (lambda (it) (plist-get it :rgtype)) (seq-filter (lambda (it) (and (plist-get it :rgtype) (string= (plist-get it :language) language))) dumb-jump-language-file-exts)))) (defun dumb-jump-get-rg-nontype-exts-by-language (language) "Return list of file extensions for a LANGUAGE that have no rgtype. These extensions need to be explicitly added as glob patterns since they are not covered by ripgrep's built-in type definitions." (seq-uniq (mapcar (lambda (it) (plist-get it :ext)) (seq-filter (lambda (it) (and (plist-get it :ext) (null (plist-get it :rgtype)) (string= (plist-get it :language) language))) dumb-jump-language-file-exts)))) (defun dumb-jump-get-git-grep-type-by-language (language) "Return list of git grep type argument for a LANGUAGE." (seq-uniq (mapcar (lambda (it) (plist-get it :ext)) (seq-filter (lambda (it) (and (plist-get it :ext) (string= (plist-get it :language) language))) dumb-jump-language-file-exts)))) (defun dumb-jump-get-rules-by-language (language searcher) "Return a list of rules for the LANGUAGE by SEARCHER." (let* ((searcher-str (cond ((eq 'git-grep searcher) "git-grep") ((eq 'rg searcher) "rg") ((eq 'ag searcher) "ag") ((eq 'git-grep-plus-ag searcher) "ag") (t "grep"))) (results (seq-filter (lambda (it) (and (string= (plist-get it ':language) language) (member searcher-str (plist-get it ':supports)))) dumb-jump-find-rules))) (if dumb-jump-functions-only (seq-filter (lambda (it) (string= (plist-get it ':type) "function")) results) results))) ;;;###autoload (define-minor-mode dumb-jump-local-mode "Buffer-local minor mode for jumping to variable and function definitions. Use this to selectively enable dumb-jump in specific major modes, e.g.: (add-hook \\='python-mode-hook #\\='dumb-jump-local-mode) For enabling globally, use `dumb-jump-mode' instead." :global nil :keymap dumb-jump-mode-map) ;;;###autoload (define-minor-mode dumb-jump-mode "Minor mode for jumping to variable and function definitions." ;; Note: independent of the Emacs xref interface. :global t :keymap dumb-jump-mode-map) ;; ----------------------------------------------------------------------------- ;;; Xref Backend (unless dumb-jump-disable-obsolete-warnings (dolist (obsolete '(dumb-jump-mode dumb-jump-go dumb-jump-go-prefer-external-other-window dumb-jump-go-prompt dumb-jump-quick-look dumb-jump-go-other-window dumb-jump-go-current-window dumb-jump-go-prefer-external)) (make-obsolete obsolete (format "`%s' has been obsoleted by the xref interface." obsolete) "2020-06-26")) (make-obsolete 'dumb-jump-back "`dumb-jump-back' has been obsoleted by `xref-pop-marker-stack'." "2020-06-26")) (cl-defmethod xref-backend-identifier-at-point ((_backend (eql dumb-jump))) (let ((bounds (bounds-of-thing-at-point 'symbol))) (and bounds (let* ((ident (dumb-jump-get-point-symbol)) (start (car bounds)) (col (- start (line-beginning-position))) (line (dumb-jump-get-point-line)) (ctx (dumb-jump-get-point-context line ident col))) (propertize ident :dumb-jump-ctx ctx))))) (cl-defmethod xref-backend-definitions ((_backend (eql dumb-jump)) entered-name) (let* ((info (dumb-jump-get-results entered-name)) (results (plist-get info :results)) (look-for (or entered-name (plist-get info :symbol))) (proj-root (plist-get info :root)) (issue (plist-get info :issue)) (lang (plist-get info :lang)) (processed (dumb-jump-process-results results (plist-get info :file) proj-root (plist-get info :ctx-type) look-for nil nil lang)) (results (plist-get processed :results)) (do-var-jump (plist-get processed :do-var-jump)) (var-to-jump (plist-get processed :var-to-jump)) (match-cur-file-front (plist-get processed :match-cur-file-front))) (dumb-jump-debug-message look-for (plist-get info :ctx-type) var-to-jump (pp-to-string match-cur-file-front) (pp-to-string results) match-cur-file-front proj-root (plist-get info :file)) (cond ((eq issue 'nogrep) (dumb-jump-message "Please install ag, rg, git grep or grep!")) ((eq issue 'nosymbol) (dumb-jump-message "No symbol under point.")) ((string-suffix-p " file" lang) (dumb-jump-message "Could not find rules for '%s'." lang)) ((= (length results) 0) (dumb-jump-message "'%s' %s %s declaration not found." look-for (if (string-blank-p (or lang "")) "with unknown language so" lang) (plist-get info :ctx-type))) (t (mapcar (lambda (res) (with-no-warnings (xref-make (plist-get res :context) (xref-make-file-location (let* ((path (plist-get res :path)) (remote-prefix (file-remote-p default-directory))) (if remote-prefix (if (file-name-absolute-p path) (concat remote-prefix path) (concat default-directory path)) (expand-file-name path))) (plist-get res :line) 0)))) (if do-var-jump (list var-to-jump) match-cur-file-front)))))) (cl-defmethod xref-backend-references ((_backend (eql dumb-jump)) entered-name) (let* ((dumb-jump--search-mode 'references) (info (dumb-jump-get-results entered-name)) (results (plist-get info :results)) (look-for (or entered-name (plist-get info :symbol))) (proj-root (plist-get info :root)) (issue (plist-get info :issue)) (lang (plist-get info :lang)) (processed (dumb-jump-process-results results (plist-get info :file) proj-root (plist-get info :ctx-type) look-for nil nil lang)) (results (plist-get processed :results)) (match-cur-file-front (plist-get processed :match-cur-file-front))) (cond ((eq issue 'nogrep) (dumb-jump-message "Please install ag, rg, git grep or grep!")) ((eq issue 'nosymbol) (dumb-jump-message "No symbol under point.")) ((string-suffix-p " file" lang) (dumb-jump-message "Could not find rules for '%s'." lang)) ((= (length results) 0) (dumb-jump-message "'%s' %s references not found." look-for (if (string-blank-p (or lang "")) "with unknown language so" lang))) (t (mapcar (lambda (res) (with-no-warnings (xref-make (plist-get res :context) (xref-make-file-location (let* ((path (plist-get res :path)) (remote-prefix (file-remote-p default-directory))) (if remote-prefix (if (file-name-absolute-p path) (concat remote-prefix path) (concat default-directory path)) (expand-file-name path))) (plist-get res :line) 0)))) match-cur-file-front))))) (cl-defmethod xref-backend-apropos ((_backend (eql dumb-jump)) pattern) (xref-backend-definitions 'dumb-jump pattern)) (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql dumb-jump))) nil) ;;;###autoload (defun dumb-jump-xref-activate () "Function to activate xref backend. Add this function to `xref-backend-functions' to dumb jump to be activated, whenever it finds a project. It is recommended to add it to the end, so that it only gets activated when no better option is found." (and (dumb-jump-get-project-root default-directory) 'dumb-jump)) (provide 'dumb-jump) ;;; dumb-jump.el ends here