From 423638a2ac2275916a9a68b4e97ec6e032fc3a74 Mon Sep 17 00:00:00 2001 From: kj Date: Tue, 8 Dec 2020 14:00:23 -0400 Subject: [PATCH] =?UTF-8?q?Agregado=20el=20ir=20a=20la=20definici=C3=B3n?= =?UTF-8?q?=20mediante=20M-.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dumb-jump-autoloads.el | 88 + elpa/dumb-jump-20201205.1625/dumb-jump-pkg.el | 2 + elpa/dumb-jump-20201205.1625/dumb-jump.el | 3090 +++++++++++++++++ elpa/dumb-jump-20201205.1625/dumb-jump.elc | Bin 0 -> 122943 bytes init.el | 3 +- 5 files changed, 3182 insertions(+), 1 deletion(-) create mode 100644 elpa/dumb-jump-20201205.1625/dumb-jump-autoloads.el create mode 100644 elpa/dumb-jump-20201205.1625/dumb-jump-pkg.el create mode 100644 elpa/dumb-jump-20201205.1625/dumb-jump.el create mode 100644 elpa/dumb-jump-20201205.1625/dumb-jump.elc diff --git a/elpa/dumb-jump-20201205.1625/dumb-jump-autoloads.el b/elpa/dumb-jump-20201205.1625/dumb-jump-autoloads.el new file mode 100644 index 0000000..f341134 --- /dev/null +++ b/elpa/dumb-jump-20201205.1625/dumb-jump-autoloads.el @@ -0,0 +1,88 @@ +;;; dumb-jump-autoloads.el --- automatically extracted autoloads +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "dumb-jump" "dumb-jump.el" (0 0 0 0)) +;;; Generated autoloads from dumb-jump.el + +(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)) + +(autoload 'dumb-jump-back "dumb-jump" "\ +Jump back to where the last jump was done. + +\(fn)" t nil) + +(autoload 'dumb-jump-quick-look "dumb-jump" "\ +Run dumb-jump-go in quick look mode. That is, show a tooltip of where it would jump instead. + +\(fn)" t nil) + +(autoload 'dumb-jump-go-other-window "dumb-jump" "\ +Like 'dumb-jump-go' but use 'find-file-other-window' instead of 'find-file'. + +\(fn)" t nil) + +(autoload 'dumb-jump-go-current-window "dumb-jump" "\ +Like dumb-jump-go but always use 'find-file'. + +\(fn)" t nil) + +(autoload 'dumb-jump-go-prefer-external "dumb-jump" "\ +Like dumb-jump-go but prefer external matches from the current file. + +\(fn)" t nil) + +(autoload 'dumb-jump-go-prompt "dumb-jump" "\ +Like dumb-jump-go but prompts for function instead of using under point + +\(fn)" t nil) + +(autoload 'dumb-jump-go-prefer-external-other-window "dumb-jump" "\ +Like dumb-jump-go-prefer-external but use 'find-file-other-window' instead of 'find-file'. + +\(fn)" t nil) + +(autoload 'dumb-jump-go "dumb-jump" "\ +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. + +\(fn &optional USE-TOOLTIP PREFER-EXTERNAL PROMPT)" t nil) + +(defvar dumb-jump-mode nil "\ +Non-nil if Dumb-Jump mode is enabled. +See the `dumb-jump-mode' command +for a description of this minor mode.") + +(custom-autoload 'dumb-jump-mode "dumb-jump" nil) + +(autoload 'dumb-jump-mode "dumb-jump" "\ +Minor mode for jumping to variable and function definitions + +\(fn &optional ARG)" t nil) + +(autoload 'dumb-jump-xref-activate "dumb-jump" "\ +Function to activate xref backend. +Add this function to `xref-backend-functions' to dumb jump to be +activiated, 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. + +\(fn)" nil nil) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "dumb-jump" '("dumb-jump-"))) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; dumb-jump-autoloads.el ends here diff --git a/elpa/dumb-jump-20201205.1625/dumb-jump-pkg.el b/elpa/dumb-jump-20201205.1625/dumb-jump-pkg.el new file mode 100644 index 0000000..e9c812a --- /dev/null +++ b/elpa/dumb-jump-20201205.1625/dumb-jump-pkg.el @@ -0,0 +1,2 @@ +;;; -*- no-byte-compile: t -*- +(define-package "dumb-jump" "20201205.1625" "Jump to definition for 40+ languages without configuration" '((emacs "24.3") (s "1.11.0") (dash "2.9.0") (popup "0.5.3")) :commit "ff9fc9360d39f5e07c1f480f8b0656b49606781b" :keywords '("programming") :authors '(("jack angers and contributors")) :maintainer '("jack angers and contributors") :url "https://github.com/jacktasia/dumb-jump") diff --git a/elpa/dumb-jump-20201205.1625/dumb-jump.el b/elpa/dumb-jump-20201205.1625/dumb-jump.el new file mode 100644 index 0000000..4e042ac --- /dev/null +++ b/elpa/dumb-jump-20201205.1625/dumb-jump.el @@ -0,0 +1,3090 @@ +;;; dumb-jump.el --- Jump to definition for 40+ languages without configuration -*- lexical-binding: t; -*- +;; Copyright (C) 2015-2019 jack angers +;; Author: jack angers and contributors +;; Url: https://github.com/jacktasia/dumb-jump +;; Package-Version: 20201205.1625 +;; Package-Commit: ff9fc9360d39f5e07c1f480f8b0656b49606781b +;; Version: 0.5.3 +;; Package-Requires: ((emacs "24.3") (s "1.11.0") (dash "2.9.0") (popup "0.5.3")) +;; 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 40+ 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: +(unless (require 'xref nil :noerror) + (require 'etags)) +(require 's) +(require 'dash) +(require 'popup) +(require 'cl-generic nil :noerror) +(require 'cl-lib) + +(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)) + +(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 + "When true will jump in a visible window if that window already has the file open." + :group 'dumb-jump + :type 'boolean) + +(defcustom dumb-jump-selector + 'popup + "Which selector to use when there is multiple choices. `ivy` and `helm' are also supported." + :group 'dumb-jump + :type '(choice (const :tag "Popup" popup) + (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 to use 'ag, 'rg, 'git-grep, 'gnu-grep,or 'grep. +If nil then the most optimal searcher will be chosen at runtime." + :group 'dumb-jump + :type '(choice (const :tag "Best Available" nil) + (const :tag "ag" ag) + (const :tag "rg" rg) + (const :tag "grep" gnu-grep) + (const :tag "git grep" git-grep) + (const :tag "git grep + ag" git-grep-plus-ag))) + +(defcustom dumb-jump-force-searcher + nil + "Forcibly use searcher: 'ag, 'rg, 'git-grep, 'gnu-grep, or 'grep. +Set to nil to not force anything and use `dumb-jump-prefer-searcher' +or most optimal searcher." + :group 'dumb-jump + :type '(choice (const :tag "Best Available" nil) + (const :tag "ag" ag) + (const :tag "rg" rg) + (const :tag "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 the path to the silver searcher. By default assumes it is in path. If not found fallbacks to grep." + :group 'dumb-jump + :type 'string) + +(defcustom dumb-jump-rg-cmd + "rg" + "The the path to ripgrep. By default assumes it is in path. If not found fallbacks to grep." + :group 'dumb-jump + :type 'string) + +(defcustom dumb-jump-git-grep-cmd + "git grep" + "The the path to git grep. By default assumes it is in path. If not found fallbacks to grep." + :group 'dumb-jump + :type 'string) + +(defcustom dumb-jump-ag-word-boundary + "(?![a-zA-Z0-9\\?\\*-])" + "`\\b` thinks `-` is a word boundary. When this matters use `\\j` instead and ag will use this value." + :group 'dumb-jump + :type 'string) + +(defcustom dumb-jump-rg-word-boundary + "($|[^a-zA-Z0-9\\?\\*-])" + "`\\b` thinks `-` is a word boundary. When this matters use `\\j` instead and rg will use this value." + :group 'dumb-jump + :type 'string) + +(defcustom dumb-jump-git-grep-word-boundary + "($|[^a-zA-Z0-9\\?\\*-])" + "`\\b` thinks `-` is a word boundary. When this matters use `\\j` instead and git grep will use this value." + :group 'dumb-jump + :type 'string) + +(defcustom dumb-jump-grep-word-boundary + "($|[^a-zA-Z0-9\\?\\*-])" + "`\\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 to searching the fallback regex." + :group 'dumb-jump + :type 'boolean) + +(defcustom dumb-jump-force-grep + nil + "When t will use grep even if ag is available." + :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 being warned to use ag 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*." + :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 searcher 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-find-rules + '((:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "elisp" + :regex "\\\((defun|cl-defun)\\s+JJJ\\j" + ;; \\j usage see `dumb-jump-ag-word-boundary` + :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)")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "elisp" + :regex "\\\(defvar\\b\\s*JJJ\\j" + :tests ("(defvar test " "(defvar test\n") + :not ("(defvar tester" "(defvar test?" "(defvar test-")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "elisp" + :regex "\\\(defcustom\\b\\s*JJJ\\j" + :tests ("(defcustom test " "(defcustom test\n") + :not ("(defcustom tester" "(defcustom test?" "(defcustom test-")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "elisp" + :regex "\\\(setq\\b\\s*JJJ\\j" :tests ("(setq test 123)") + :not ("setq test-blah 123)" "(setq tester" "(setq test?" "(setq test-")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "elisp" + :regex "\\\(JJJ\\s+" :tests ("(let ((test 123)))") :not ("(let ((test-2 123)))")) + + ;; variable in method signature + (:type "variable" :supports ("ag" "rg" "git-grep") :language "elisp" + :regex "\\((defun|cl-defun)\\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 + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "commonlisp" + :regex "\\\(defun\\s+JJJ\\j" + ;; \\j usage see `dumb-jump-ag-word-boundary` + :tests ("(defun test (blah)" "(defun test\n") + :not ("(defun test-asdf (blah)" "(defun test-blah\n" + "(defun tester (blah)" "(defun test? (blah)" "(defun test- (blah)")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "commonlisp" + :regex "\\\(defparameter\\b\\s*JJJ\\j" + :tests ("(defparameter test " "(defparameter test\n") + :not ("(defparameter tester" "(defparameter test?" "(defparameter test-")) + + ;; racket + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "racket" + :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")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "racket" + :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")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "racket" + :regex "\\\(let\\s+JJJ\\s*(\\\(|\\\[)*" + :tests ("(let test ((blah foo) (bar bas))" "(let test\n" "(let test [(foo") + :not ("(let ((test blah")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "racket" + :regex "\\\(define\\s+JJJ\\j" + :tests ("(define test " "(define test\n") + :not ("(define (test")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "racket" + :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")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "racket" + :regex "\\\(lambda\\s+\\\(?[^\(\)]*\\s*JJJ\\j\\s*\\\)?" + :tests ("(lambda (test)" "(lambda (foo test)" "(lambda test (foo)") + :not ("(lambda () test")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "racket" + :regex "\\\(define\\s+\\\([^\(\)]+\\s*JJJ\\j\\s*\\\)?" + :tests ("(define (foo test)" "(define (foo test bar)") + :not ("(define foo test" "(define (test foo" "(define (test)")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "racket" + :regex "\\(struct\\s+JJJ\\j" + :tests ("(struct test (a b)")) + + ;; scheme + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "scheme" + :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")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "scheme" + :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")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "scheme" + :regex "\\\(let\\s+JJJ\\s*(\\\(|\\\[)*" + :tests ("(let test ((blah foo) (bar bas))" "(let test\n" "(let test [(foo") + :not ("(let ((test blah")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "scheme" + :regex "\\\(define\\s+JJJ\\j" + :tests ("(define test " "(define test\n") + :not ("(define (test")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "scheme" + :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")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "scheme" + :regex "\\\(lambda\\s+\\\(?[^\(\)]*\\s*JJJ\\j\\s*\\\)?" + :tests ("(lambda (test)" "(lambda (foo test)" "(lambda test (foo)") + :not ("(lambda () test")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "scheme" + :regex "\\\(define\\s+\\\([^\(\)]+\\s*JJJ\\j\\s*\\\)?" + :tests ("(define (foo test)" "(define (foo test bar)") + :not ("(define foo test" "(define (test foo" "(define (test)")) + + ;; c++ + (:type "function" :supports ("ag" "rg" "git-grep") :language "c++" + :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();")) + + ;; (:type "variable" :supports ("grep") :language "c++" + ;; :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;")) + + (:type "variable" :supports ("ag" "rg") :language "c++" + :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;")) + + (:type "type" :supports ("ag" "rg" "git-grep") :language "c++" + :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 + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "clojure" + :regex "\\(def\\s+JJJ\\j" + :tests ("(def test (foo)")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "clojure" + :regex "\\(defn-?\\s+JJJ\\j" + :tests ("(defn test [foo]" "(defn- test [foo]") + :not ("(defn test? [foo]" "(defn- test? [foo]")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "clojure" + :regex "\\(defmacro\\s+JJJ\\j" + :tests ("(defmacro test [foo]")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "clojure" + :regex "\\(deftask\\s+JJJ\\j" + :tests ("(deftask test [foo]")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "clojure" + :regex "\\(deftype\\s+JJJ\\j" + :tests ("(deftype test [foo]")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "clojure" + :regex "\\(defmulti\\s+JJJ\\j" + :tests ("(defmulti test fn")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "clojure" + :regex "\\(defmethod\\s+JJJ\\j" + :tests ("(defmethod test type")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "clojure" + :regex "\\(definterface\\s+JJJ\\j" + :tests ("(definterface test (foo)")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "clojure" + :regex "\\(defprotocol\\s+JJJ\\j" + :tests ("(defprotocol test (foo)")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "clojure" + :regex "\\(defrecord\\s+JJJ\\j" + :tests ("(defrecord test [foo]")) + + ;; coffeescript + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "coffeescript" + :regex "^\\s*JJJ\\s*[=:].*[-=]>" + :tests ("test = () =>" "test= =>" "test = ->" "test=()->" + "test : () =>" "test: =>" "test : ->" "test:()->") + :not ("# test = =>" "test = 1")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "coffeescript" + :regex "^\\s*JJJ\\s*[:=][^:=-][^>]+$" + :tests ("test = $" "test : [" "test = {" "test = a") + :not ("test::a" "test: =>" "test == 1" "# test = 1")) + + (:type "class" :supports ("ag" "grep" "rg" "git-grep") :language "coffeescript" + :regex "^\\s*\\bclass\\s+JJJ" + :tests ("class test" "class test extends") + :not ("# class")) + + ;; obj-c + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "objc" + :regex "\\\)\\s*JJJ(:|\\b|\\s)" + :tests ("- (void)test" "- (void)test:(UIAlertView *)alertView") + :not ("- (void)testnot" "- (void)testnot:(UIAlertView *)alertView")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "objc" + :regex "\\b\\*?JJJ\\s*=[^=\\n]+" + :tests ("NSString *test = @\"asdf\"") + :not ("NSString *testnot = @\"asdf\"" "NSString *nottest = @\"asdf\"")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "objc" + :regex "(@interface|@protocol|@implementation)\\b\\s*JJJ\\b\\s*" + :tests ("@interface test: UIWindow") + :not ("@interface testnon: UIWindow")) + + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "objc" + :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 + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "swift" + :regex "(let|var)\\s*JJJ\\s*(=|:)[^=:\\n]+" + :tests ("let test = 1234" "var test = 1234" "private lazy var test: UITapGestureRecognizer") + :not ("if test == 1234:")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "swift" + :regex "func\\s+JJJ\\b\\s*(<[^>]*>)?\\s*\\(" + :tests ("func test(asdf)" "func test()" "func test()") + :not ("func testnot(asdf)" "func testnot()")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "swift" + :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)")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "swift" + :regex "(typealias)\\s+JJJ\\b\\s*?=" + :tests ("typealias test =") + :not ("typealias testnot")) + + ;; c# + (:type "function" :supports ("ag" "rg") :language "csharp" + :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()")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "csharp" + :regex "\\s*\\bJJJ\\s*=[^=\\n)]+" :tests ("int test = 1234") :not ("if test == 1234:" "int nottest = 44")) + + (: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) + (:type "function" :supports ("ag" "rg") :language "java" + :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()")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "java" + :regex "\\s*\\bJJJ\\s*=[^=\\n)]+" :tests ("int test = 1234") :not ("if test == 1234:" "int nottest = 44")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "java" + :regex "(class|interface)\\s*JJJ\\b" + :tests ("class test:" "public class test implements Something") + :not ("class testnot:" "public class testnot implements Something")) + + ;; vala (again just like c#, exactly the same..) + (:type "function" :supports ("ag" "rg") :language "vala" + :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()")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "vala" + :regex "\\s*\\bJJJ\\s*=[^=\\n)]+" :tests ("int test = 1234") :not ("if test == 1234:" "int nottest = 44")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "vala" + :regex "(class|interface)\\s*JJJ\\b" + :tests ("class test:" "public class test : IReadableChannel, I") + :not ("class testnot:" "public class testnot : IReadableChannel, I")) + + ;; coq + (:type "function" :supports ("ag" "rg" "git-grep") :language "coq" + :regex "\\s*Variable\\s+JJJ\\b" + :tests ("Variable test") + :not ("Variable testx")) + + (:type "function" :supports ("ag" "rg" "git-grep") :language "coq" + :regex "\\s*Inductive\\s+JJJ\\b" + :tests ("Inductive test") + :not ("Inductive testx")) + + (:type "function" :supports ("ag" "rg" "git-grep") :language "coq" + :regex "\\s*Lemma\\s+JJJ\\b" + :tests ("Lemma test") + :not ("Lemma testx")) + + (:type "function" :supports ("ag" "rg" "git-grep") :language "coq" + :regex "\\s*Definition\\s+JJJ\\b" + :tests ("Definition test") + :not ("Definition testx")) + + (:type "function" :supports ("ag" "rg" "git-grep") :language "coq" + :regex "\\s*Hypothesis\\s+JJJ\\b" + :tests ("Hypothesis test") + :not ("Hypothesis testx")) + + (:type "function" :supports ("ag" "rg" "git-grep") :language "coq" + :regex "\\s*Theorm\\s+JJJ\\b" + :tests ("Theorm test") + :not ("Theorm testx")) + + (:type "function" :supports ("ag" "rg" "git-grep") :language "coq" + :regex "\\s*Fixpoint\\s+JJJ\\b" + :tests ("Fixpoint test") + :not ("Fixpoint testx")) + + (:type "function" :supports ("ag" "rg" "git-grep") :language "coq" + :regex "\\s*Module\\s+JJJ\\b" + :tests ("Module test") + :not ("Module testx")) + + (:type "function" :supports ("ag" "rg" "git-grep") :language "coq" + :regex "\\s*CoInductive\\s+JJJ\\b" + :tests ("CoInductive test") + :not ("CoInductive testx")) + + ;; python + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "python" + :regex "\\s*\\bJJJ\\s*=[^=\\n]+" + :tests ("test = 1234") + :not ("if test == 1234:" "_test = 1234")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "python" + :regex "def\\s*JJJ\\b\\s*\\\(" + :tests ("\tdef test(asdf)" "def test()") + :not ("\tdef testnot(asdf)" "def testnot()")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "python" + :regex "class\\s*JJJ\\b\\s*\\\(?" + :tests ("class test(object):" "class test:") + :not ("class testnot:" "class testnot(object):")) + + ;; matlab + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "matlab" + :regex "^\\s*\\bJJJ\\s*=[^=\\n]+" + :tests ("test = 1234") + :not ("for test = 1:2:" "_test = 1234")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "matlab" + :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()")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "matlab" + :regex "^\\s*classdef\\s*JJJ\\b\\s*" + :tests ("classdef test") + :not ("classdef testnot")) + + ;; nim + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "nim" + :regex "(const|let|var)\\s*JJJ\\s*(=|:)[^=:\\n]+" + :tests ("let test = 1234" "var test = 1234" "var test: Stat" "const test = 1234") + :not ("if test == 1234:")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "nim" + :regex "(proc|func|macro|template)\\s*`?JJJ`?\\b\\s*\\\(" + :tests ("\tproc test(asdf)" "proc test()" "func test()" "macro test()" "template test()") + :not ("\tproc testnot(asdf)" "proc testnot()")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "nim" + :regex "type\\s*JJJ\\b\\s*(\\{[^}]+\\})?\\s*=\\s*\\w+" + :tests ("type test = object" "type test {.pure.} = enum") + :not ("type testnot = object")) + + ;; nix + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "nix" + :regex "\\b\\s*JJJ\\s*=[^=;]+" + :tests ("test = 1234;" "test = 123;" "test=123") + :not ("testNot = 1234;" "Nottest = 1234;" "AtestNot = 1234;")) + + ;; ruby + (:type "variable" :supports ("ag" "rg" "git-grep") :language "ruby" + :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")) + + (:type "function" :supports ("ag" "rg" "git-grep") :language "ruby" + :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")) + + (:type "function" :supports ("ag" "rg" "git-grep") :language "ruby" + :regex "(^|\\W)define(_singleton|_instance)?_method(\\s|[(])\\s*:JJJ($|[^\\w|:])" + :tests ("define_method(:test, &body)" + "mod.define_instance_method(:test) { body }")) + + (:type "type" :supports ("ag" "rg" "git-grep") :language "ruby" + :regex "(^|[^\\w.])class\\s+(\\w*::)*JJJ($|[^\\w|:])" + :tests ("class test" "class Foo::test")) + + (:type "type" :supports ("ag" "rg" "git-grep") :language "ruby" + :regex "(^|[^\\w.])module\\s+(\\w*::)*JJJ($|[^\\w|:])" + :tests ("module test" "module Foo::test")) + + (:type "function" :supports ("ag" "rg" "git-grep") :language "ruby" + :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 + (:type "variable" :supports ("ag" "rg" "git-grep") :language "groovy" + :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")) + + (:type "function" :supports ("ag" "rg" "git-grep") :language "groovy" + :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")) + + (:type "type" :supports ("ag" "rg" "git-grep") :language "groovy" + :regex "(^|[^\\w.])class\\s+(\\w*::)*JJJ($|[^\\w|:])" + :tests ("class test" "class Foo::test")) + + ;; crystal + (:type "variable" :supports ("ag" "rg" "git-grep") :language "crystal" + :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")) + + (:type "function" :supports ("ag" "rg" "git-grep") :language "crystal" + :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")) + + (:type "type" :supports ("ag" "rg" "git-grep") :language "crystal" + :regex "(^|[^\\w.])class\\s+(\\w*::)*JJJ($|[^\\w|:])" + :tests ("class test" "class Foo::test")) + + (:type "type" :supports ("ag" "rg" "git-grep") :language "crystal" + :regex "(^|[^\\w.])module\\s+(\\w*::)*JJJ($|[^\\w|:])" + :tests ("module test" "module Foo::test")) + + (:type "type" :supports ("ag" "rg" "git-grep") :language "crystal" + :regex "(^|[^\\w.])struct\\s+(\\w*::)*JJJ($|[^\\w|:])" + :tests ("struct test" "struct Foo::test")) + + (:type "type" :supports ("ag" "rg" "git-grep") :language "crystal" + :regex "(^|[^\\w.])alias\\s+(\\w*::)*JJJ($|[^\\w|:])" + :tests ("alias test" "alias Foo::test")) + + ;; scad + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "scad" + :regex "\\s*\\bJJJ\\s*=[^=\\n]+" :tests ("test = 1234") :not ("if test == 1234 {")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "scad" + :regex "function\\s*JJJ\\s*\\\(" + :tests ("function test()" "function test ()")) + + (:type "module" :supports ("ag" "grep" "rg" "git-grep") :language "scad" + :regex "module\\s*JJJ\\s*\\\(" + :tests ("module test()" "module test ()")) + + ;; scala + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "scala" + :regex "\\bval\\s*JJJ\\s*=[^=\\n]+" :tests ("val test = 1234") :not ("case test => 1234")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "scala" + :regex "\\bvar\\s*JJJ\\s*=[^=\\n]+" :tests ("var test = 1234") :not ("case test => 1234")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "scala" + :regex "\\btype\\s*JJJ\\s*=[^=\\n]+" :tests ("type test = 1234") :not ("case test => 1234")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "scala" + :regex "\\bdef\\s*JJJ\\s*\\\(" + :tests ("def test(asdf)" "def test()")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "scala" + :regex "class\\s*JJJ\\s*\\\(?" + :tests ("class test(object)")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "scala" + :regex "trait\\s*JJJ\\s*\\\(?" + :tests ("trait test(object)")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "scala" + :regex "object\\s*JJJ\\s*\\\(?" + :tests ("object test(object)")) + + ;; R + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "r" + :regex "\\bJJJ\\s*=[^=><]" :tests ("test = 1234") :not ("if (test == 1234)")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "r" + :regex "\\bJJJ\\s*<-\\s*function\\b" + :tests ("test <- function" "test <- function(") + :not ("test <- functionX")) + + ;; perl + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "perl" + :regex "sub\\s*JJJ\\s*(\\{|\\()" + :tests ("sub test{" "sub test {" "sub test(" "sub test (")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "perl" + :regex "JJJ\\s*=\\s*" + :tests ("$test = 1234")) + + ;; Tcl + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "tcl" + :regex "proc\\s+JJJ\\s*\\{" + :tests ("proc test{" "proc test {")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "tcl" + :regex "set\\s+JJJ" + :tests ("set test 1234")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "tcl" + :regex "(variable|global)\\s+JJJ" + :tests ("variable test" "global test")) + + ;; shell + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "shell" + :regex "function\\s*JJJ\\s*" + :tests ("function test{" "function test {" "function test () {") + :not ("function nottest {")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "shell" + :regex "JJJ\\\(\\\)\\s*\\{" + :tests ("test() {") + :not ("testx() {")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "shell" + :regex "\\bJJJ\\s*=\\s*" + :tests ("test = 1234") :not ("blahtest = 1234")) + + ;; php + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "php" + :regex "function\\s*JJJ\\s*\\\(" + :tests ("function test()" "function test ()")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "php" + :regex "\\*\\s@method\\s+[^ \t]+\\s+JJJ\\(" + :tests ("/** @method string|false test($a)" " * @method bool test()")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "php" + :regex "(\\s|->|\\$|::)JJJ\\s*=\\s*" + :tests ("$test = 1234" "$foo->test = 1234")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "php" + :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")) + (:type "trait" :supports ("ag" "grep" "rg" "git-grep") :language "php" + :regex "trait\\s*JJJ\\s*\\\{" + :tests ("trait test{" "trait test {")) + + (:type "interface" :supports ("ag" "grep" "rg" "git-grep") :language "php" + :regex "interface\\s*JJJ\\s*\\\{" + :tests ("interface test{" "interface test {")) + + (:type "class" :supports ("ag" "grep" "rg" "git-grep") :language "php" + :regex "class\\s*JJJ\\s*(extends|implements|\\\{)" + :tests ("class test{" "class test {" "class test extends foo" "class test implements foo")) + + ;; dart + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "dart" + :regex "\\bJJJ\\s*\\([^()]*\\)\\s*[{]" + :tests ("test(foo) {" "test (foo){" "test(foo){")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "dart" + :regex "class\\s*JJJ\\s*[\\\(\\\{]" + :tests ("class test(object) {" "class test{")) + + ;; faust + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "faust" + :regex "\\bJJJ\(\\\(.+\\\)\)*\\s*=" + :tests ("test = osc + 0.5;" "test(freq) = osc(freq) + 0.5;")) + + ;; fortran + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "fortran" + :regex "\\s*\\bJJJ\\s*=[^=\\n]+" + :tests ("test = 1234") + :not ("if (test == 1234)")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "fortran" + :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")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "fortran" + :regex "^\\s*(interface|INTERFACE)\\s+JJJ\\b" + :tests ("interface test" "INTERFACE test") + :not ("interface test2" "end interface test" "INTERFACE test2" + "END INTERFACE test")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "fortran" + :regex "^\\s*(module|MODULE)\\s+JJJ\\s*" + :tests ("module test" "MODULE test") + :not ("end module test" "END MODULE test")) + + ;; go + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "go" + :regex "\\s*\\bJJJ\\s*=[^=\\n]+" :tests ("test = 1234") :not ("if test == 1234 {")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "go" + :regex "\\s*\\bJJJ\\s*:=\\s*" :tests ("test := 1234")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "go" + :regex "func\\s+\\\([^\\\)]*\\\)\\s+JJJ\\s*\\\(" + :tests ("func (s *blah) test(filename string) string {")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "go" + :regex "func\\s+JJJ\\s*\\\(" + :tests ("func test(url string) (string, error)")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "go" + :regex "type\\s+JJJ\\s+struct\\s+\\\{" + :tests ("type test struct {")) + + ;; javascript extended + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "javascript" + :regex "(service|factory)\\\(['\"]JJJ['\"]" :tags ("angular") + :tests ("module.factory('test', [\"$rootScope\", function($rootScope) {")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "javascript" + :regex "\\bJJJ\\s*[=:]\\s*\\\([^\\\)]*\\\)\\s+=>" :tags ("es6") + :tests ("const test = (foo) => " "test: (foo) => {" " test: (foo) => {")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "javascript" + :regex "\\bJJJ\\s*\\([^()]*\\)\\s*[{]" :tags ("es6") + :tests ("test(foo) {" "test (foo){" "test(foo){") + :not ("test = blah.then(function(){")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "javascript" :tags ("es6") + :regex "class\\s*JJJ\\s*[\\\(\\\{]" + :tests ("class test(object) {" "class test{")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "javascript" :tags ("es6") + :regex "class\\s*JJJ\\s+extends" + :tests ("class test extends Component{")) + + ;; javascript + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "javascript" + :regex "\\s*\\bJJJ\\s*=[^=\\n]+" :tests ("test = 1234" "const test = props =>") :not ("if (test === 1234)")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "javascript" + :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)")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "javascript" + :regex "function\\s*JJJ\\s*\\\(" + :tests ("function test()" "function test ()")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "javascript" + :regex "\\bJJJ\\s*:\\s*function\\s*\\\(" + :tests ("test: function()")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "javascript" + :regex "\\bJJJ\\s*=\\s*function\\s*\\\(" + :tests ("test = function()")) + + ;; hcl terraform + (:type "block" :supports ("ag" "grep" "rg" "git-grep") :language "hcl" + :regex "(variable|output|module)\\s*\"JJJ\"\\s*\\\{" + :tests ("variable \"test\" {" + "output \"test\" {" + "module \"test\" {")) + + (:type "block" :supports ("ag" "grep" "rg" "git-grep") :language "hcl" + :regex "(data|resource)\\s*\"\\w+\"\\s*\"JJJ\"\\s*\\\{" + :tests ("data \"openstack_images_image_v2\" \"test\" {" + "resource \"google_compute_instance\" \"test\" {")) + + ;; typescript + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "typescript" + :regex "(service|factory)\\\(['\"]JJJ['\"]" :tags ("angular") + :tests ("module.factory('test', [\"$rootScope\", function($rootScope) {")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "typescript" + :regex "\\bJJJ\\s*[=:]\\s*\\\([^\\\)]*\\\)\\s+=>" + :tests ("const test = (foo) => " "test: (foo) => {" " test: (foo) => {")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "typescript" + :regex "\\bJJJ\\s*\\([^()]*\\)\\s*[{]" + :tests ("test(foo) {" "test (foo){" "test(foo){") + :not ("test = blah.then(function(){")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "typescript" + :regex "class\\s*JJJ\\s*[\\\(\\\{]" + :tests ("class test{")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "typescript" + :regex "class\\s*JJJ\\s+extends" + :tests ("class test extends Component{")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "typescript" + :regex "function\\s*JJJ\\s*\\\(" + :tests ("function test()" "function test ()")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "typescript" + :regex "\\bJJJ\\s*:\\s*function\\s*\\\(" + :tests ("test: function()")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "typescript" + :regex "\\bJJJ\\s*=\\s*function\\s*\\\(" + :tests ("test = function()")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "typescript" + :regex "\\s*\\bJJJ\\s*=[^=\\n]+" :tests ("test = 1234" "const test = props =>") :not ("if (test === 1234)")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "typescript" + :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 + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "julia" + :regex "(@noinline|@inline)?\\s*function\\s*JJJ(\\{[^\\}]*\\})?\\(" + :tests ("function test()" "@inline function test()" + "function test{T}(h)")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "julia" + :regex "(@noinline|@inline)?JJJ(\\{[^\\}]*\\})?\\([^\\)]*\\)\s*=" + :tests ("test(a)=1" "test(a,b)=1*8" + "@noinline test()=1" "test{T}(x)=x")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "julia" + :regex "macro\\s*JJJ\\(" + :tests ("macro test(a)=1" " macro test(a,b)=1*8")) + + (:type "variable" :supports ("ag" "rg") :language "julia" + :regex "const\\s+JJJ\\b" + :tests ("const test = ")) + + (:type "type" :supports ("ag" "rg") :language "julia" + :regex "(mutable)?\\s*struct\\s*JJJ" + :tests ("struct test")) + + (:type "type" :supports ("ag" "rg") :language "julia" + :regex "(type|immutable|abstract)\\s*JJJ" + :tests ("type test" "immutable test" "abstract test <:Testable" )) + + ;; haskell + (:type "module" :supports ("ag") :language "haskell" + :regex "^module\\s+JJJ\\s+" + :tests ("module Test (exportA, exportB) where")) + + ; TODO Doesn't support any '=' in arguments. E.g. 'foo A{a = b,..} = bar'. + (:type "top level function" :supports ("ag") :language "haskell" + :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")) + + (:type "type-like" :supports ("ag") :language "haskell" + :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. + (:type "(data)type constructor 1" :supports ("ag") :language "haskell" + :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 + (:type "type" :supports ("ag" "rg") :language "ocaml" + :regex "^\\s*(and|type)\\s+.*\\bJJJ\\b" + :tests ("type test =" + "and test =" + "type 'a test =" + "type ('a, _, 'c) test")) + + (:type "variable" :supports ("ag" "rg") :language "ocaml" + :regex "let\\s+JJJ\\b" + :tests ("let test =" + "let test x y =")) + + (:type "variable" :supports ("ag" "rg") :language "ocaml" + :regex "let\\s+rec\\s+JJJ\\b" + :tests ("let rec test =" + "let rec test x y =")) + + (:type "variable" :supports ("ag" "rg") :language "ocaml" + :regex "\\s*val\\s*\\bJJJ\\b\\s*" + :tests ("val test")) + + (:type "module" :supports ("ag" "rg") :language "ocaml" + :regex "^\\s*module\\s*\\bJJJ\\b" + :tests ("module test =")) + + (:type "module" :supports ("ag" "rg") :language "ocaml" + :regex "^\\s*module\\s*type\\s*\\bJJJ\\b" + :tests ("module type test =")) + + ;; lua + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "lua" + :regex "\\s*\\bJJJ\\s*=[^=\\n]+" :tests ("test = 1234") :not ("if test === 1234")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "lua" + :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)")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "lua" + :regex "function\\s*JJJ\\s*\\\(" + :tests ("function test()" "function test ()")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "lua" + :regex "function\\s*.+[.:]JJJ\\s*\\\(" + :tests ("function MyClass.test()" "function MyClass.test ()" + "function MyClass:test()" "function MyClass:test ()")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "lua" + :regex "\\bJJJ\\s*=\\s*function\\s*\\\(" + :tests ("test = function()")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "lua" + :regex "\\b.+\\.JJJ\\s*=\\s*function\\s*\\\(" + :tests ("MyClass.test = function()")) + + ;; rust + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "rust" + :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);")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "rust" + :regex "\\bconst\\s+JJJ:\\s*[^=\\n]+\\s*=[^=\\n]+" + :tests ("const test: u32 = 1234;")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "rust" + :regex "\\bstatic\\s+(mut\\s+)?JJJ:\\s*[^=\\n]+\\s*=[^=\\n]+" + :tests ("static test: u32 = 1234;" + "static mut test: u32 = 1234;")) + + ;; variable in method signature + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "rust" + :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 + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "rust" + :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 + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "rust" + :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 + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "rust" + :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 }")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "rust" + :regex "\\bfn\\s+JJJ\\s*\\\(" + :tests ("fn test(asdf: u32)" "fn test()" "pub fn test()")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "rust" + :regex "\\bmacro_rules!\\s+JJJ" + :tests ("macro_rules! test")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "rust" + :regex "struct\\s+JJJ\\s*[{\\\(]?" + :tests ("struct test(u32, u32)" + "struct test;" + "struct test { abc: u32, def: Vec }")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "rust" + :regex "trait\\s+JJJ\\s*[{]?" + :tests ("trait test;" "trait test { fn abc() -> u32; }")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "rust" + :regex "\\btype\\s+JJJ([^=\\n]+)?\\s*=[^=\\n]+;" + :tests ("type test = Rc>;" + "type test = Arc>>;")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "rust" + :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 {")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "rust" + :regex "mod\\s+JJJ\\s*[{]?" + :tests ("mod test;" "pub mod test {")) + + ;; elixir + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "elixir" + :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:")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "elixir" + :regex "\\s*JJJ\\s*=[^=\\n]+" + :tests ("test = 1234") + :not ("if test == 1234")) + + (:type "module" :supports ("ag" "grep" "rg" "git-grep") :language "elixir" + :regex "defmodule\\s+(\\w+\\.)*JJJ\\s+" + :tests ("defmodule test do" + "defmodule Foo.Bar.test do")) + + (:type "module" :supports ("ag" "grep" "rg" "git-grep") :language "elixir" + :regex "defprotocol\\s+(\\w+\\.)*JJJ\\s+" + :tests ("defprotocol test do" + "defprotocol Foo.Bar.test do")) + + ;; erlang + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "erlang" + :regex "^JJJ\\b\\s*\\\(" + :tests ("test() ->" + "test()->" + "test(Foo) ->" + "test (Foo,Bar) ->" + "test(Foo, Bar)->")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "erlang" + :regex "\\s*JJJ\\s*=[^:=\\n]+" + :tests ("test = 1234") + :not ("if test =:= 1234" + "if test == 1234")) + + (:type "module" :supports ("ag" "grep" "rg" "git-grep") :language "erlang" + :regex "^-module\\\(JJJ\\\)" + :tests ("-module(test).")) + + ;; scss + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "scss" + :regex "@mixin\\sJJJ\\b\\s*\\\(" + :tests ("@mixin test()")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "scss" + :regex "@function\\sJJJ\\b\\s*\\\(" + :tests ("@function test()")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "scss" + :regex "JJJ\\s*:\\s*" + :tests ("test :")) + + ;; sml + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "sml" + :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 =")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "sml" + :regex "\\s*val\\s+\\bJJJ\\b" + :tests ("val test =" + "val test=" + "val test : bool")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "sml" + :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 =")) + + (:type "module" :supports ("ag" "grep" "rg" "git-grep") :language "sml" + :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 + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "sql" + :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()")) + + (:type "table" :supports ("ag" "grep" "rg" "git-grep") :language "sql" + :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")) + + (:type "view" :supports ("ag" "grep" "rg" "git-grep") :language "sql" + :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")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "sql" + :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 + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "systemverilog" + :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")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "systemverilog" + :regex "\\s*task\\s+\\bJJJ\\b" + :tests ("task test (" "task test(") + :not ("task testing (" "task test2(")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "systemverilog" + :regex "\\s*\\bJJJ\\b\\s*=" + :tests ("assign test =" "assign test=" "int test =" "int test=") + :not ("assign testing =" "assign test2=")) + + (:type "function" :supports ("ag" "rg" "git-grep") :language "systemverilog" + :regex "function\\s[^\\s]+\\s*\\bJJJ\\b" + :tests ("function Matrix test ;" "function Matrix test;") + :not ("function test blah")) + + ;; matches SV class handle declarations + (:type "function" :supports ("ag" "rg" "git-grep") :language "systemverilog" + :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 + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "vhdl" + :regex "\\s*type\\s+\\bJJJ\\b" + :tests ("type test is" "type test is") + :not ("type testing is" "type test2 is")) + + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "vhdl" + :regex "\\s*constant\\s+\\bJJJ\\b" + :tests ("constant test :" "constant test:") + :not ("constant testing " "constant test2:")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "vhdl" + :regex "function\\s*\"?JJJ\"?\\s*\\\(" + :tests ("function test(signal)" "function test (signal)" "function \"test\" (signal)") + :not ("function testing(signal")) + + ;; latex + (:type "command" :supports ("ag" "grep" "rg" "git-grep") :language "tex" + :regex "\\\\.*newcommand\\\*?\\s*\\\{\\s*(\\\\)JJJ\\s*}" + :tests ("\\newcommand{\\test}" "\\renewcommand{\\test}" "\\renewcommand*{\\test}" "\\newcommand*{\\test}" "\\renewcommand{ \\test }") + :not("\\test" "test")) + + (:type "command" :supports ("ag" "grep" "rg" "git-grep") :language "tex" + :regex "\\\\.*newcommand\\\*?\\s*(\\\\)JJJ\\j" + :tests ("\\newcommand\\test {}" "\\renewcommand\\test{}" "\\newcommand \\test") + :not("\\test" "test")) + + (:type "length" :supports ("ag" "grep" "rg" "git-grep") :language "tex" + :regex "\\\\(s)etlength\\s*\\\{\\s*(\\\\)JJJ\\s*}" + :tests ("\\setlength { \\test}" "\\setlength{\\test}" "\\setlength{\\test}{morecommands}" ) + :not("\\test" "test")) + + (:type "counter" :supports ("ag" "grep" "rg" "git-grep") :language "tex" + :regex "\\\\newcounter\\\{\\s*JJJ\\s*}" + :tests ("\\newcounter{test}" ) + :not("\\test" "test")) + + (:type "environment" :supports ("ag" "grep" "rg" "git-grep") :language "tex" + :regex "\\\\.*newenvironment\\s*\\\{\\s*JJJ\\s*}" + :tests ("\\newenvironment{test}" "\\newenvironment {test}{morecommands}" "\\lstnewenvironment{test}" "\\newenvironment {test}" ) + :not("\\test" "test" )) + + ;; pascal (todo: var, type, const) + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "pascal" + :regex "\\bfunction\\s+JJJ\\b" + :tests (" function test : ")) + + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "pascal" + :regex "\\bprocedure\\s+JJJ\\b" + :tests (" procedure test ; ")) + + ;; f# + (:type "variable" :supports ("ag" "grep" "git-grep") :language "fsharp" + :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")) + + (:type "interface" :supports ("ag" "grep" "git-grep") :language "fsharp" + :regex "member(\\b.+\\.|\\s+)JJJ\\b.*\\\=" + :tests ("member test = 1234" "member this.test = 1234") + :not ("member testnot = 1234" "member this.testnot = 1234")) + + (:type "type" :supports ("ag" "grep" "git-grep") :language "fsharp" + :regex "type\\s+JJJ\\b.*\\\=" + :tests ("type test = 1234") + :not ("type testnot = 1234")) + + ;; kotlin + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "kotlin" + :regex "fun\\s*(<[^>]*>)?\\s*JJJ\\s*\\(" + :tests ("fun test()" "fun test()")) + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "kotlin" + :regex "(val|var)\\s*JJJ\\b" + :not ("val testval" "var testvar") + :tests ("val test " "var test")) + (:type "type" :supports ("ag" "grep" "rg" "git-grep") :language "kotlin" + :regex "(class|interface)\\s*JJJ\\b" + :tests ("class test" "class test : SomeInterface" "interface test")) + + ;; zig + (:type "function" :supports ("ag" "grep" "rg" "git-grep") :language "zig" + :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 {")) + + (:type "variable" :supports ("ag" "grep" "rg" "git-grep") :language "zig" + :regex "(var|const)\\s+JJJ\\b" + :tests ("const test: i32 = 3;" + "var test: i32 = 3;" + "pub const test: i32 = 3;")) + + ;; protobuf + (:type "message" :supports ("ag" "grep" "rg" "git-grep") :language "protobuf" + :regex "message\\s+JJJ\\s*\\\{" + :tests ("message test{" "message test {")) + + (:type "enum" :supports ("ag" "grep" "rg" "git-grep") :language "protobuf" + :regex "enum\\s+JJJ\\s*\\\{" + :tests ("enum test{" "enum test {"))) + + + "List of regex patttern templates organized by language and type to use for generating the grep command." + :group 'dumb-jump + :type + '(repeat + (plist + :options ((:type string) + (:supports string) + (:language string) + (:regex string) + (:tests (repeat string)) + (:not (repeat string)))))) + + ; https://github.com/ggreer/the_silver_searcher/blob/master/tests/list_file_types.t + ; https://github.com/BurntSushi/ripgrep/blob/master/ignore/src/types.rs#L99 +(defcustom dumb-jump-language-file-exts + '((: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 "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 "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 "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 "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 "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 "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)) + + "Mapping of programming language(s) to file extensions." + :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" :right "^(" :left nil) + (:language "javascript" :type "variable" :right nil :left "($") + (:language "javascript" :type "variable" :right "^)" :left "($") + (:language "javascript" :type "variable" :right "^\\." :left nil) + (:language "javascript" :type "variable" :right "^;" :left nil) + (:language "typescript" :type "function" :right "^(" :left nil) + (:language "perl" :type "function" :right "^(" :left nil) + (: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" :right "^(" :left nil) + (:language "php" :type "class" :right nil :left "new\s+") + (:language "elisp" :type "function" :right nil :left "($") + (:language "elisp" :type "variable" :right "^)" :left nil) + (:language "scheme" :type "function" :right nil :left "($") + (:language "scheme" :type "variable" :right "^)" :left 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") + "Files and directories that signify a directory is a project root." + :group 'dumb-jump + :type '(repeat (string :tag "Name"))) + +(defcustom dumb-jump-default-project "~" + "The default project to search within if a project root is not found." + :group 'dumb-jump + :type 'string) + +(defcustom dumb-jump-project nil + "The 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-before-jump-hook nil + "Hooks called before jumping." + :type 'hook + :group 'dumb-jump + :type 'hook) + +(defcustom dumb-jump-after-jump-hook nil + "Hooks called after jumping." + :type 'hook + :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." + :group 'dumb-jump + :type 'boolean) + +(defcustom dumb-jump-confirm-jump-to-modified-file + t + "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) + +(defun dumb-jump-message-prin1 (str &rest args) + "Helper function when debugging apply STR 'prin1-to-string' to all ARGS." + (apply 'message str (-map 'prin1-to-string args))) + +(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? + (s-contains? "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 is installed." + (if (eq dumb-jump--rg-installed? 'unset) + (setq dumb-jump--rg-installed? + (let ((result (s-match "ripgrep \\([0-9]+\\)\\.\\([0-9]+\\).*" + (shell-command-to-string (concat dumb-jump-rg-cmd " --version"))))) + (when (equal (length result) 3) + (let ((major (string-to-number (nth 1 result))) + (minor (string-to-number (nth 2 result)))) + (or + (and (= major 0) (>= minor 10)) + (>= major 1)))))) + 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? + (s-contains? "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 'gnu if GNU grep is installed, 'bsd if BSD grep is installed, and nil otherwise." + (if (eq dumb-jump--grep-installed? 'unset) + (let* ((version (shell-command-to-string (concat dumb-jump-grep-cmd " --version"))) + (variant (cond ((s-match "GNU grep" version) 'gnu) + ((s-match "[0-9]+\\.[0-9]+" version) 'bsd) + (t nil)))) + (setq dumb-jump--grep-installed? variant)) + dumb-jump--grep-installed?)) + +(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 content to the temporary file, run cmd on it, return result" + (with-temp-buffer + (insert test) + (write-file thefile nil) + (delete-region (point-min) (point-max)) + (shell-command realcmd t) + (delete-file thefile) + (buffer-substring-no-properties (point-min) (point-max)))) + +(defun dumb-jump-run-git-grep-test (test cmd) + "Use string TEST as input through a local, temporary file for CMD. +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 cmd " " thefile)))) + +(defun dumb-jump-run-ag-test (test cmd) + "Use TEST as input, but first write it into temporary file +and then run ag on it. 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 cmd " " thefile)))) + +(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 ((fail-tmpl "grep FAILURE '%s' %s in response '%s' | CMD: '%s' | rule: '%s'") + (variant (if (eq (dumb-jump-grep-installed?) 'gnu) 'gnu-grep 'grep))) + (-mapcat + (lambda (rule) + (-mapcat + (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 (s-contains? test resp))) + (and run-not-tests (> (length resp) 0))) + (list (format fail-tmpl (if run-not-tests "not" "") + test (if run-not-tests "IS unexpectedly" "NOT") resp cmd (plist-get rule :regex)))))) + (plist-get rule (if run-not-tests :not :tests)))) + (--filter (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" + (let ((fail-tmpl "ag FAILURE '%s' %s in response '%s' | CMD: '%s' | rule: '%s'")) + (-mapcat + (lambda (rule) + (-mapcat + (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 (s-contains? test resp))) + (and run-not-tests (> (length resp) 0))) + (list (format fail-tmpl test (if run-not-tests "IS unexpectedly" "NOT") resp cmd rule))))) + (plist-get rule (if run-not-tests :not :tests)))) + (--filter (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" + (let ((fail-tmpl "rg FAILURE '%s' %s in response '%s' | CMD: '%s' | rule: '%s'")) + (-mapcat + (lambda (rule) + (-mapcat + (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 (s-contains? test resp))) + (and run-not-tests (> (length resp) 0))) + (list (format fail-tmpl test (if run-not-tests "IS unexpectedly" "NOT") resp cmd rule))))) + (plist-get rule (if run-not-tests :not :tests)))) + (--filter (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" + (let ((fail-tmpl "rg FAILURE '%s' %s in response '%s' | CMD: '%s' | rule: '%s'")) + (-mapcat + (lambda (rule) + (-mapcat + (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 (s-contains? test resp))) + (and run-not-tests (> (length resp) 0))) + (list (format fail-tmpl test (if run-not-tests "IS unexpectedly" "NOT") resp cmd rule))))) + (plist-get rule (if run-not-tests :not :tests)))) + (--filter (member "grep" (plist-get it :supports)) dumb-jump-find-rules)))) + +(defun dumb-jump-message (str &rest args) + "Log message STR with ARGS to the *Messages* buffer if not using dumb-jump-quiet." + (when (not 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 (--find-index (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) + "Previews CANDIDATE in a temporary buffer displaying the file at the 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 result) + (format "%s:%s: %s" + (s-replace proj "" (plist-get result :path)) + (plist-get result :line) + (s-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. Ignore _PROJ." + (ivy-read "Jump to: " (-zip choices results) + :action (lambda (cand) + (dumb-jump-result-follow (cdr cand))) + :caller 'dumb-jump-ivy-jump-to-selected)) + +(defun dumb-jump-prompt-user-for-choice (proj results) + "Put a PROJ's list of RESULTS in a 'popup-menu' (or helm/ivy) +for user to select. Filters PROJ path from files for display." + (let ((choices (--map (dumb-jump--format-result proj it) results))) + (cond + ((eq dumb-jump-selector 'completing-read) + (dumb-jump-to-selected results choices (completing-read "Jump to: " choices))) + ((and (eq dumb-jump-selector 'ivy) (fboundp 'ivy-read)) + (funcall dumb-jump-ivy-jump-to-selected-function results choices proj)) + ((and (eq dumb-jump-selector 'helm) (fboundp 'helm)) + (helm :sources + (helm-build-sync-source "Jump to: " + :action '(("Jump to match" . dumb-jump-result-follow)) + :candidates (-zip choices results) + :persistent-action 'dumb-jump-helm-persist-action) + :buffer "*helm dumb jump choices*")) + (t + (dumb-jump-to-selected results choices (popup-menu* choices)))))) + +(defun dumb-jump-get-project-root (filepath) + "Keep looking at the parent dir of FILEPATH until a denoter file/dir is found." + (s-chop-suffix + "/" + (expand-file-name + (or + dumb-jump-project + (locate-dominating-file filepath #'dumb-jump-get-config) + dumb-jump-default-project)))) + +(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 (--filter + (file-exists-p (expand-file-name it dir)) + dumb-jump-project-denoters)))) + +(defun dumb-jump-get-language (file) + "Get language from FILE extension and then fallback to using 'major-mode' name." + (let* ((languages (-distinct + (--map (plist-get it :language) + dumb-jump-find-rules))) + (language (or (dumb-jump-get-language-from-mode) + (dumb-jump-get-language-by-filename file) + (dumb-jump-get-mode-base-name)))) + (if (member language languages) + language + (format ".%s file" (or (file-name-extension file) ""))))) + +(defun dumb-jump-get-mode-base-name () + "Get the base name of the mode." + (s-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'." + (let* ((lookup '(sh "shell" cperl "perl" matlab "matlab" octave "matlab")) + (m (dumb-jump-get-mode-base-name)) + (result (plist-get lookup (intern m)))) + result)) + + +(defun dumb-jump-get-language-by-filename (file) + "Get the programming language from the FILE." + (let* ((filename (if (s-ends-with? ".gz" file) + (file-name-sans-extension file) + file)) + (result (--filter + (s-ends-with? (concat "." (plist-get it :ext)) filename) + dumb-jump-language-file-exts))) + (when result + (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 prompt) + "Run dumb-jump-fetch-results if searcher installed, buffer is saved, and there's a symbol under point." + (cond + ((not (or (dumb-jump-ag-installed?) + (dumb-jump-rg-installed?) + (dumb-jump-git-grep-installed?) + (dumb-jump-grep-installed?))) + (dumb-jump-issue-result "nogrep")) + ((or (string= (buffer-name) "*shell*") + (string= (buffer-name) "*eshell*")) + (dumb-jump-fetch-shell-results prompt)) + ((and (not prompt) (not (region-active-p)) (not (thing-at-point 'symbol))) + (dumb-jump-issue-result "nosymbol")) + (t + (dumb-jump-fetch-file-results prompt)))) + +(defun dumb-jump-fetch-shell-results (&optional prompt) + (let* ((cur-file (buffer-name)) + (proj-root (dumb-jump-get-project-root default-directory)) + (proj-config (dumb-jump-get-config proj-root)) + (config (when (s-ends-with? ".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 prompt))) + +(defun dumb-jump-fetch-file-results (&optional prompt) + (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 (s-ends-with? ".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 prompt))) + +(defun dumb-jump-process-symbol-by-lang (lang look-for) + "Process LANG's LOOK-FOR. For instance, clojure needs namespace part removed." + (cond + ((and (string= lang "clojure") (s-contains? "/" look-for)) + (nth 1 (s-split "/" look-for))) + ((and (string= lang "ruby") (s-contains? "::" look-for)) + (-last-item (s-split "::" look-for))) + ((and (or (string= lang "ruby") (string= lang "crystal")) (s-starts-with? ":" look-for)) + (s-chop-prefix ":" look-for)) + ((and (string= lang "systemverilog") (s-starts-with? "`" look-for)) + (s-chop-prefix "`" look-for)) + (t + look-for))) + +(defun dumb-jump-get-point-line () + "Get line at point." + (if (version< emacs-version "24.4") + (thing-at-point 'line) + (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)) + (if (version< emacs-version "24.4") + (thing-at-point 'symbol) + (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 (--filter (s-match (concat "\\." (plist-get it :ext) "\\b") buffer-contents) + dumb-jump-language-file-exts))) + (--map (plist-get it :language) found))) + +(defun dumb-jump-fetch-results (cur-file proj-root lang _config &optional prompt) + "Return a list of results based on current file context and calling grep/ag. +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." + (let* ((cur-line-num (line-number-at-pos)) + (proj-config (dumb-jump-get-config proj-root)) + (config (when (s-ends-with? ".dumbjump" proj-config) + (dumb-jump-read-config proj-root proj-config))) + (found-symbol (or prompt (dumb-jump-get-point-symbol))) + (look-for (dumb-jump-process-symbol-by-lang lang found-symbol)) + (pt-ctx (if prompt + (get-text-property 0 :dumb-jump-ctx prompt) + (dumb-jump-get-point-context + (dumb-jump-get-point-line) + look-for + (dumb-jump--get-symbol-start)))) + (ctx-type + (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)) + (generate-fn (plist-get gen-funcs :generate)) + (searcher (plist-get gen-funcs :searcher)) + + (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))) + ; we will search proj root and all include paths + (search-paths (-distinct (-concat (list proj-root) include-paths))) + ; run command for all + (raw-results (--mapcat + ;; TODO: should only pass exclude paths to actual project root + (dumb-jump-run-command look-for it regexes lang exclude-paths cur-file + cur-line-num parse-fn generate-fn) + search-paths)) + + (results (delete-dups (--map (plist-put it :target look-for) raw-results)))) + + `(: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." + (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." + (interactive) + (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'." + (interactive) + (let ((dumb-jump-window 'other)) + (dumb-jump-go))) + +;;;###autoload +(defun dumb-jump-go-current-window () + "Like dumb-jump-go but always use 'find-file'." + (interactive) + (let ((dumb-jump-window 'current)) + (dumb-jump-go))) + +;;;###autoload +(defun dumb-jump-go-prefer-external () + "Like dumb-jump-go but prefer external matches from the current file." + (interactive) + (dumb-jump-go nil t)) + +;;;###autoload +(defun dumb-jump-go-prompt () + "Like dumb-jump-go but prompts for function instead of using under point" + (interactive) + (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 use 'find-file-other-window' instead of 'find-file'." + (interactive) + (let ((dumb-jump-window 'other)) + (dumb-jump-go-prefer-external))) + +;;;###autoload +(defun dumb-jump-go (&optional use-tooltip prefer-external prompt) + "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." + (interactive "P") + (let* ((start-time (float-time)) + (info (dumb-jump-get-results prompt)) + (end-time (float-time)) + (fetch-time (- end-time start-time)) + (results (plist-get info :results)) + (look-for (or prompt (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.")) + ((s-ends-with? " 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)) + ((= result-count 0) + (dumb-jump-message "'%s' %s %s declaration not found." look-for (if (s-blank? lang) "with unknown language so" lang) (plist-get info :ctx-type)))))) + +(defcustom dumb-jump-language-comments + '((:comment "//" :language "c++") + (:comment ";" :language "elisp") + (:comment ";" :language "commonlisp") + (:comment "//" :language "javascript") + (:comment "//" :language "typescript") + (:comment "//" :language "dart") + (: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 "fortran") + (:comment "//" :language "go") + (:comment "//" :language "zig") + (:comment "#" :language "perl") + (:comment "#" :language "tcl") + (:comment "//" :language "php") + (: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 "shell") + (: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")) + "List of one-line comments organized by language." + :group 'dumb-jump + :type + '(repeat + (plist + :options ((:comment string) + (:language string))))) + +(defun dumb-jump-get-comment-by-language (lang) + "Yields the one-line comment for the given LANG." + (let* ((entries (-distinct + (--filter (string= (plist-get it :language) lang) + dumb-jump-language-comments)))) + (if (= 1 (length entries)) + (plist-get (car entries) :comment) + nil))) + +(defun dumb-jump-filter-no-start-comments (results lang) + "Filter out RESULTS with a :context that starts with a comment +given the LANG of the current file." + (let ((comment (dumb-jump-get-comment-by-language lang))) + (if comment + (-concat + (--filter (not (s-starts-with? comment (s-trim (plist-get it :context)))) results)) + results))) + +(defun dumb-jump-handle-results + (results cur-file proj-root ctx-type look-for use-tooltip prefer-external) + "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." + (let* ((processed (dumb-jump-process-results results cur-file proj-root ctx-type look-for use-tooltip prefer-external)) + (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 + (popup-menu* (--map (dumb-jump--format-result proj-root it) results))) + (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-process-results + (results cur-file proj-root ctx-type _look-for _use-tooltip prefer-external) + "Process (filter, sort, ...) 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." + "Figure which of the RESULTS to jump to. Favoring the CUR-FILE" + (let* ((lang (dumb-jump-get-language-by-filename cur-file)) + (match-sorted (-sort (lambda (x y) (< (plist-get x :diff) (plist-get y :diff))) results)) + (match-no-comments (dumb-jump-filter-no-start-comments match-sorted lang)) + + ;; Find the relative current file path by the project root. In some cases the results will + ;; not be absolute but relative and the "current file" filters must match in both + ;; cases. Also works when current file is in an arbitrary sub folder. + (rel-cur-file + (cond ((and (s-starts-with? proj-root cur-file) + (s-starts-with? default-directory cur-file)) + (substring cur-file (length default-directory) (length cur-file))) + + ((and (s-starts-with? proj-root cur-file) + (not (s-starts-with? default-directory cur-file))) + (substring cur-file (1+ (length proj-root)) (length cur-file))) + + (t + cur-file))) + + ;; Moves current file results to the front of the list, unless PREFER-EXTERNAL then put + ;; them last. + (match-cur-file-front + (if (not prefer-external) + (-concat + (--filter (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) + (--filter (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) + + ;; Sort non-current files by path length so the nearest file is more likely to be + ;; sorted higher to the top. Also sorts by line number for sanity. + (-sort (lambda (x y) + (and (< (plist-get x :line) (plist-get y :line)) + (< (length (plist-get x :path)) (length (plist-get y :path))))) + (--filter (not (or (string= (plist-get it :path) cur-file) + (string= (plist-get it :path) rel-cur-file))) + match-no-comments))) + (-concat + (-sort (lambda (x y) + (and (< (plist-get x :line) (plist-get y :line)) + (< (length (plist-get x :path)) (length (plist-get y :path))))) + (--filter (not (or (string= (plist-get it :path) cur-file) + (string= (plist-get it :path) rel-cur-file))) + match-no-comments)) + (--filter (or (string= (plist-get it :path) cur-file) + (string= (plist-get it :path) rel-cur-file)) + match-no-comments)))) + + (matches + (if (not prefer-external) + (-distinct + (append (dumb-jump-current-file-results cur-file match-cur-file-front) + (dumb-jump-current-file-results rel-cur-file match-cur-file-front))) + match-cur-file-front)) + + (var-to-jump (car matches)) + ;; TODO: handle if ctx-type is null but ALL results are variable + + ;; When non-aggressive it should only jump when there is only one match, regardless of + ;; context. + (do-var-jump + (and (or dumb-jump-aggressive + (= (length match-cur-file-front) 1)) + (or (= (length matches) 1) + (string= ctx-type "variable") + (string= ctx-type "")) + var-to-jump))) + + (list :results results + :do-var-jump do-var-jump + :var-to-jump var-to-jump + :match-cur-file-front match-cur-file-front))) + +(defun dumb-jump-read-config (root config-file) + "Load and return options (exclusions, inclusions, etc). +Ffrom the ROOT project CONFIG-FILE." + (with-temp-buffer + (insert-file-contents (expand-file-name config-file root)) + (let ((local-root (if (file-remote-p root) + (tramp-file-name-localname + (tramp-dissect-file-name root)) + root)) + include exclude lang) + (while (not (eobp)) + (cond ((looking-at "^language \\\(.+\\\)") + (setq lang (match-string 1))) + ((looking-at "^\\+\\(.+\\)") + (push (expand-file-name (match-string 1) local-root) + include)) + ((looking-at "^-/?\\(.+\\)") + (push (expand-file-name (match-string 1) local-root) + exclude))) + (forward-line)) + (list :exclude (nreverse exclude) + :include (nreverse include) + :language lang)))) + +(defun dumb-jump-file-modified-p (path) + "Check if PATH is currently open in Emacs and has a modified buffer." + (interactive) + (--any? + (and (buffer-modified-p it) + (buffer-file-name it) + (file-exists-p (buffer-file-name it)) + (file-equal-p (buffer-file-name it) path)) + (buffer-list))) + +(defun dumb-jump-result-follow (result &optional use-tooltip proj) + "Take the RESULT to jump to and record the jump, for jumping back, and then trigger jump. If dumb-jump-confirm-jump-to-modified-file is t, prompt if we should continue if destination has been modified. If it is nil, display a warning." + (if (dumb-jump-file-modified-p (plist-get result :path)) + (let ((target-file (plist-get result :path))) + (if dumb-jump-confirm-jump-to-modified-file + (when (y-or-n-p (concat target-file " has been modified so we may have the wrong location. Continue?")) + (dumb-jump--result-follow result use-tooltip proj)) + (progn (message + "Warning: %s has been modified so we may have the wrong location." + target-file) + (dumb-jump--result-follow result use-tooltip proj)))) + (dumb-jump--result-follow result use-tooltip proj))) + +(defun dumb-jump--result-follow (result &optional use-tooltip proj) + "Take the RESULT to jump to and record the jump, for jumping back, and then trigger jump." + (let* ((target-boundary (s-matched-positions-all + (concat "\\b" (regexp-quote (plist-get result :target)) "\\b") + (plist-get result :context))) + ;; column pos is either via tpos from ag or by using the regex above or last using old s-index-of + (pos (if target-boundary + (car (car target-boundary)) + (s-index-of (plist-get result :target) (plist-get result :context)))) + + (result-path (plist-get result :path)) + + ;; Return value is either a string like "/ssh:user@1.2.3.4:" or nil + (tramp-path-prefix (file-remote-p default-directory)) + + ;; If result-path is an absolute path, the prefix is added to the head of it, + ;; or result-path is added to the end of default-directory + (path-for-tramp (when (and result-path tramp-path-prefix) + (if (file-name-absolute-p result-path) + (concat tramp-path-prefix result-path) + (concat default-directory result-path)))) + + (thef (or path-for-tramp result-path)) + (line (plist-get result :line))) + (when thef + (if use-tooltip + (popup-tip (dumb-jump--format-result proj result)) + (dumb-jump-goto-file-line thef line pos))) + ;; return the file for test + thef)) + + +(defun dumb-jump-goto-file-line (thefile theline pos) + "Open THEFILE and go line THELINE" + (if (fboundp 'xref-push-marker-stack) + (xref-push-marker-stack) + (ring-insert find-tag-marker-ring (point-marker))) + + (with-demoted-errors "Error running `dumb-jump-before-jump-hook': %S" + (run-hooks 'dumb-jump-before-jump-hook)) + + (let* ((visible-buffer (find-buffer-visiting thefile)) + (visible-window (when visible-buffer (get-buffer-window visible-buffer)))) + (cond + ((and visible-window dumb-jump-use-visible-window) + (select-window visible-window)) + ((eq dumb-jump-window 'other) + (find-file-other-window thefile)) + (t (find-file thefile)))) + + (goto-char (point-min)) + (forward-line (1- theline)) + (forward-char pos) + (with-demoted-errors "Error running `dumb-jump-after-jump-hook': %S" + (run-hooks 'dumb-jump-after-jump-hook))) + +(defun dumb-jump-current-file-results (path results) + "Return the PATH's RESULTS." + (let ((matched (--filter (string= path (plist-get it :path)) results))) + matched)) + +(defun dumb-jump-generators-by-searcher (searcher) + "For a SEARCHER it yields a response parser, a command +generator function, an installed? function, and the corresponding +searcher symbol." + (cond ((equal 'git-grep searcher) + `(:parse ,'dumb-jump-parse-git-grep-response + :generate ,'dumb-jump-generate-git-grep-command + :installed ,'dumb-jump-git-grep-installed? + :searcher ,searcher)) + ((equal 'ag searcher) + `(:parse ,'dumb-jump-parse-ag-response + :generate ,'dumb-jump-generate-ag-command + :installed ,'dumb-jump-ag-installed? + :searcher ,searcher)) + ((equal 'git-grep-plus-ag searcher) + `(:parse ,'dumb-jump-parse-ag-response + :generate ,'dumb-jump-generate-git-grep-plus-ag-command + :installed ,'dumb-jump-git-grep-plus-ag-installed? + :searcher ,searcher)) + ((equal 'rg searcher) + `(:parse ,'dumb-jump-parse-rg-response + :generate ,'dumb-jump-generate-rg-command + :installed ,'dumb-jump-rg-installed? + :searcher ,searcher)) + ((equal 'gnu-grep searcher) + `(:parse ,'dumb-jump-parse-grep-response + :generate ,'dumb-jump-generate-gnu-grep-command + :installed ,'dumb-jump-grep-installed? + :searcher ,searcher)) + ((equal 'grep searcher) + `(:parse ,'dumb-jump-parse-grep-response + :generate ,'dumb-jump-generate-grep-command + :installed ,'dumb-jump-grep-installed? + :searcher ,searcher)))) + +(defun dumb-jump-pick-grep-variant (&optional proj-root) + (cond + ;; If `dumb-jump-force-searcher' is not nil then use that searcher. + (dumb-jump-force-searcher + (dumb-jump-generators-by-searcher dumb-jump-force-searcher)) + + ;; If project root has a .git then use git-grep if installed. + ((and proj-root + (dumb-jump-git-grep-installed?) + (file-exists-p (expand-file-name ".git" proj-root))) + (dumb-jump-generators-by-searcher 'git-grep)) + + ;; If `dumb-jump-prefer-searcher' is not nil then use if installed. + ((and dumb-jump-prefer-searcher + (funcall (plist-get (dumb-jump-generators-by-searcher dumb-jump-prefer-searcher) + :installed))) + (dumb-jump-generators-by-searcher dumb-jump-prefer-searcher)) + + ;; Fallback searcher order. + ((dumb-jump-ag-installed?) + (dumb-jump-generators-by-searcher 'ag)) + ((dumb-jump-rg-installed?) + (dumb-jump-generators-by-searcher 'rg)) + ((eq (dumb-jump-grep-installed?) 'gnu) + (dumb-jump-generators-by-searcher 'gnu-grep)) + (t + (dumb-jump-generators-by-searcher 'grep)))) + +(defun dumb-jump-shell-command-switch () + "Yields the shell command switch to use for the current + `shell-file-name' in order to not load the shell profile/RC for + speeding up things." + (let ((base-name (downcase (file-name-base shell-file-name)))) + (cond + ((or (string-equal "zsh" base-name) + (string-equal "csh" base-name) + (string-equal "tcsh" base-name)) + "-icf") + + ((string-equal "bash" base-name) + "-c") + + (t + shell-command-switch)))) + +;; TODO: rename dumb-jump-run-definition-command +(defun dumb-jump-run-command + (look-for proj regexes lang exclude-args cur-file line-num parse-fn generate-fn) + "Run the grep command based on the needle LOOK-FOR in the directory TOSEARCH" + (let* ((proj-root (if (file-remote-p proj) + (directory-file-name + (tramp-file-name-localname (tramp-dissect-file-name proj))) + proj)) + (cmd (funcall generate-fn look-for cur-file proj-root regexes lang exclude-args)) + (shell-command-switch (dumb-jump-shell-command-switch)) + (rawresults (shell-command-to-string cmd))) + + (dumb-jump-debug-message cmd rawresults) + (when (and (s-blank? rawresults) dumb-jump-fallback-search) + (setq regexes (list dumb-jump-fallback-regex)) + (setq cmd (funcall generate-fn look-for cur-file proj-root regexes lang exclude-args)) + (setq rawresults (shell-command-to-string cmd)) + (dumb-jump-debug-message cmd rawresults)) + (unless (s-blank? cmd) + (let ((results (funcall parse-fn rawresults cur-file line-num))) + (--filter (s-contains? look-for (plist-get it :context)) results))))) + +(defun dumb-jump-parse-response-line (resp-line cur-file) + "Parse a search program's single RESP-LINE for CUR-FILE into a list of (path line context)." + (let* ((parts (--remove (string= it "") + (s-split "\\(?:^\\|:\\)[0-9]+:" resp-line))) + (line-num-raw (s-match "\\(?:^\\|:\\)\\([0-9]+\\):" resp-line))) + + (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))) + ((s-match ":[0-9]+:$" resp-line) + nil) + ((and parts line-num-raw) + (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))) + (nth 1 line-num-raw) (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))) + (nth 1 line-num-raw) (nth 0 parts))))))) + +(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 (--mapcat (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 (-non-nil records))) + (--filter + (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) + "Takes a grep response RESP and parses into a list of plists." + (let* ((resp-no-warnings (--filter (and (not (s-starts-with? "grep:" it)) + (not (s-contains? "No such file or" it))) + (s-split "\n" (s-trim resp)))) + (parsed (--map (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-parse-ag-response (resp cur-file cur-line-num) + "Takes a ag response RESP and parses into a list of plists." + (let* ((resp-lines (s-split "\n" (s-trim resp))) + (parsed (--map (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) + "Takes a rg response RESP and parses into a list of plists." + (let* ((resp-lines (s-split "\n" (s-trim resp))) + (parsed (--map (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) + "Takes a git grep response RESP and parses into a list of plists." + (let* ((resp-lines (s-split "\n" (s-trim resp))) + (parsed (--map (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) + (s-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." + (let* ((contexts (--filter (string= (plist-get it ':language) lang) dumb-jump-language-contexts)) + (usable-ctxs + (when (> (length contexts) 0) + (--filter (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 (--filter + (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) + "Generate the --include grep argument of file extensions by LANGUAGE." + (let ((exts (dumb-jump-get-file-exts-by-language language))) + (dumb-jump-arg-joiner + "--include" + (--map (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 ((args (s-join (format " %s " prefix) values))) + (if (and args 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 (variable, function, etc)." + (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 + (--filter (string= (plist-get it :type) ctx-type) raw-rules) + raw-rules)) + (rules (or ctx-rules raw-rules)) + (regexes (--map (plist-get it :regex) rules))) + regexes)) + +(defun dumb-jump-populate-regex (it look-for variant) + "Populate IT regex template with LOOK-FOR." + (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 (s-replace "\\j" boundary text)) + (when (eq variant 'gnu-grep) + (setq text (s-replace "\\s" "[[:space:]]" text))) + (setq text (s-replace "JJJ" (regexp-quote look-for) text)) + (when (and (eq variant 'rg) (string-prefix-p "-" text)) + (setq text (concat "[-]" (substring text 1)))) + text))) + +(defun dumb-jump-populate-regexes (look-for regexes variant) + "Take list of REGEXES and populate the LOOK-FOR target and return that list." + (--map (dumb-jump-populate-regex it look-for variant) regexes)) + +(defun dumb-jump-generate-ag-command (look-for cur-file proj regexes lang exclude-paths) + "Generate the ag response based on the needle LOOK-FOR in the directory PROJ." + (let* ((filled-regexes (dumb-jump-populate-regexes look-for regexes 'ag)) + (agtypes (dumb-jump-get-ag-type-by-language lang)) + (lang-exts (dumb-jump-get-file-exts-by-language lang)) + (proj-dir (file-name-as-directory proj)) + ;; TODO: --search-zip always? in case the include is the in gz area like emacs lisp code. + (cmd (concat dumb-jump-ag-cmd + " --nocolor --nogroup" + (if (s-ends-with? ".gz" cur-file) + " --search-zip" + "") + (when (not (s-blank? dumb-jump-ag-search-args)) + (concat " " dumb-jump-ag-search-args)) + (if agtypes + (s-join "" (--map (format " --%s" it) agtypes)) + ;; there can only be one `-G` arg + (concat " -G '(" + (s-join "|" (--map (format "\\.%s" it) lang-exts)) + ")$'")))) + (exclude-args (dumb-jump-arg-joiner + "--ignore-dir" (--map (shell-quote-argument (s-replace proj-dir "" it)) exclude-paths))) + (regex-args (shell-quote-argument (s-join "|" filled-regexes)))) + (if (= (length regexes) 0) + "" + (dumb-jump-concat-command cmd exclude-args regex-args proj)))) + +(defun dumb-jump-get-git-grep-files-matching-symbol (symbol proj-root) + "Search for the literal SYMBOL in the PROJ-ROOT via git grep for a list of file matches." + (let* ((cmd (format "git grep --full-name -F -c %s %s" (shell-quote-argument symbol) proj-root)) + (result (s-trim (shell-command-to-string cmd))) + (matched-files (--map (first (s-split ":" it)) + (s-split "\n" result)))) + matched-files)) + +(defun dumb-jump-format-files-as-ag-arg (files proj-root) + "Take a list of FILES and their PROJ-ROOT and return a `ag -G` argument." + (format "'(%s)'" (s-join "|" (--map (file-relative-name + (expand-file-name it proj-root)) + files)))) + +(defun dumb-jump-get-git-grep-files-matching-symbol-as-ag-arg (symbol proj-root) + "Get the files matching the SYMBOL via `git grep` in the PROJ-ROOT and return them formatted for `ag -G`." + (dumb-jump-format-files-as-ag-arg + (dumb-jump-get-git-grep-files-matching-symbol symbol proj-root) + proj-root)) + +;; git-grep plus ag only recommended for huge repos like the linux kernel +(defun dumb-jump-generate-git-grep-plus-ag-command (look-for cur-file proj regexes _lang exclude-paths) + "Generate the ag response based on the needle LOOK-FOR in the directory PROJ. +Using ag to search only the files found via git-grep literal symbol search." + (let* ((filled-regexes (dumb-jump-populate-regexes look-for regexes 'ag)) + (proj-dir (file-name-as-directory proj)) + (ag-files-arg (dumb-jump-get-git-grep-files-matching-symbol-as-ag-arg look-for proj-dir)) + (cmd (concat dumb-jump-ag-cmd + " --nocolor --nogroup" + (if (s-ends-with? ".gz" cur-file) + " --search-zip" + "") + " -G " ag-files-arg + " ")) + (exclude-args (dumb-jump-arg-joiner + "--ignore-dir" (--map (shell-quote-argument (s-replace proj-dir "" it)) exclude-paths))) + (regex-args (shell-quote-argument (s-join "|" filled-regexes)))) + (if (= (length regexes) 0) + "" + (dumb-jump-concat-command cmd exclude-args regex-args proj)))) + +(defun dumb-jump-generate-rg-command (look-for _cur-file proj regexes lang exclude-paths) + "Generate the rg response based on the needle LOOK-FOR in the directory PROJ." + (let* ((filled-regexes (dumb-jump-populate-regexes look-for regexes 'rg)) + (rgtypes (dumb-jump-get-rg-type-by-language lang)) + (proj-dir (file-name-as-directory proj)) + (cmd (concat dumb-jump-rg-cmd + " --color never --no-heading --line-number -U" + (when (not (s-blank? dumb-jump-rg-search-args)) + (concat " " dumb-jump-rg-search-args)) + (s-join "" (--map (format " --type %s" it) rgtypes)))) + (exclude-args (dumb-jump-arg-joiner + "-g" (--map (shell-quote-argument (concat "!" (s-replace proj-dir "" it))) exclude-paths))) + (regex-args (shell-quote-argument (s-join "|" filled-regexes)))) + (if (= (length regexes) 0) + "" + (dumb-jump-concat-command cmd exclude-args regex-args proj)))) + +(defun dumb-jump-generate-git-grep-command (look-for cur-file proj regexes lang exclude-paths) + "Generate the git grep response based on the needle LOOK-FOR in the directory PROJ." + (let* ((filled-regexes (dumb-jump-populate-regexes look-for regexes 'git-grep)) + (ggtypes (when (file-name-extension cur-file) (dumb-jump-get-git-grep-type-by-language lang))) + (cmd (concat dumb-jump-git-grep-cmd + " --color=never --line-number" + (when dumb-jump-git-grep-search-untracked + " --untracked") + (when (not (s-blank? dumb-jump-git-grep-search-args)) + (concat " " dumb-jump-git-grep-search-args)) + " -E")) + (fileexps (s-join " " (or (--map (shell-quote-argument (format "%s/*.%s" proj it)) ggtypes) '(":/")))) + (exclude-args (s-join " " + (--map (shell-quote-argument (concat ":(exclude)" it)) + exclude-paths))) + (regex-args (shell-quote-argument (s-join "|" filled-regexes)))) + (if (= (length regexes) 0) + "" + (dumb-jump-concat-command cmd regex-args "--" fileexps exclude-args)))) + +(defun dumb-jump-generate-grep-command (look-for cur-file proj regexes lang exclude-paths) + "Find LOOK-FOR's CUR-FILE in the PROJ with REGEXES for the LANG but not in EXCLUDE-PATHS." + (let* ((filled-regexes (--map (shell-quote-argument it) + (dumb-jump-populate-regexes look-for regexes 'grep))) + (cmd (concat (if (eq system-type 'windows-nt) "" (concat dumb-jump-grep-prefix " ")) + (if (s-ends-with? ".gz" cur-file) + dumb-jump-zgrep-cmd + dumb-jump-grep-cmd))) + (exclude-args (dumb-jump-arg-joiner "--exclude-dir" exclude-paths)) + (include-args (dumb-jump-get-ext-includes lang)) + (regex-args (dumb-jump-arg-joiner "-e" filled-regexes))) + (if (= (length regexes) 0) + "" + (dumb-jump-concat-command cmd dumb-jump-grep-args exclude-args include-args regex-args proj)))) + +(defun dumb-jump-generate-gnu-grep-command (look-for cur-file proj regexes _lang _exclude-paths) + "Find LOOK-FOR's CUR-FILE in the PROJ with REGEXES for the LANG but not in EXCLUDE-PATHS." + (let* ((filled-regexes (--map (shell-quote-argument it) + (dumb-jump-populate-regexes look-for regexes 'gnu-grep))) + (cmd (concat (if (eq system-type 'windows-nt) "" (concat dumb-jump-grep-prefix " ")) + (if (s-ends-with? ".gz" cur-file) + dumb-jump-zgrep-cmd + dumb-jump-grep-cmd))) + ;; TODO: GNU grep doesn't support these, so skip them + (exclude-args "") + (include-args "") + (regex-args (dumb-jump-arg-joiner "-e" filled-regexes))) + (if (= (length regexes) 0) + "" + (dumb-jump-concat-command cmd dumb-jump-gnu-grep-args exclude-args include-args regex-args proj)))) + +(defun dumb-jump-concat-command (&rest parts) + "Concat the PARTS of a command if each part has a length." + (s-join " " (-map #'s-trim (--filter (> (length it) 0) parts)))) + +(defun dumb-jump-get-file-exts-by-language (language) + "Return list of file extensions for a LANGUAGE." + (--map (plist-get it :ext) + (--filter (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." + (-distinct (--map (plist-get it :agtype) + (--filter (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." + (-distinct (--map (plist-get it :rgtype) + (--filter (and + (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." + (-distinct (--map (plist-get it :ext) + (--filter (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 (--filter (and + (string= (plist-get it ':language) language) + (member searcher-str (plist-get it ':supports))) + dumb-jump-find-rules))) + (if dumb-jump-functions-only + (--filter (string= (plist-get it ':type) "function") results) + results))) + +;;;###autoload +(define-minor-mode dumb-jump-mode + "Minor mode for jumping to variable and function definitions" + :global t + :keymap dumb-jump-mode-map) + + +;;; Xref Backend +(when (featurep 'xref) + (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 + dumb-jump-go-current-window)) + (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 (point-at-bol))) + (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)) prompt) + (let* ((info (dumb-jump-get-results prompt)) + (results (plist-get info :results)) + (look-for (or prompt (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)) + (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.")) + ((s-ends-with? " 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 (s-blank? lang) "with unknown language so" lang) (plist-get info :ctx-type))) + (t (mapcar (lambda (res) + (xref-make + (plist-get res :context) + (xref-make-file-location + (expand-file-name (plist-get res :path)) + (plist-get res :line) + 0))) + (if do-var-jump + (list var-to-jump) + 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 +activiated, 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 diff --git a/elpa/dumb-jump-20201205.1625/dumb-jump.elc b/elpa/dumb-jump-20201205.1625/dumb-jump.elc new file mode 100644 index 0000000000000000000000000000000000000000..c876225e84e8b2f7c1ea52d9c95a37a55a942a32 GIT binary patch literal 122943 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