From 9e62898d92f3e504cc3f0501d9d69eb809bbdc0a Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Mon, 26 Mar 2018 00:02:22 -0400 Subject: [PATCH] completion/ivy: major refactor of file-search commands --- modules/completion/ivy/autoload/evil.el | 200 ++++------------------ modules/completion/ivy/autoload/ivy.el | 202 +++++++++++++++++++++++ modules/config/default/+bindings.el | 10 +- modules/config/default/+evil-commands.el | 8 +- 4 files changed, 245 insertions(+), 175 deletions(-) diff --git a/modules/completion/ivy/autoload/evil.el b/modules/completion/ivy/autoload/evil.el index a44cb24d1..f9c00262e 100644 --- a/modules/completion/ivy/autoload/evil.el +++ b/modules/completion/ivy/autoload/evil.el @@ -16,188 +16,52 @@ ;; --- file searching --------------------- -(defvar +ivy--file-last-search nil) -(defvar +ivy--file-search-recursion-p t) -(defvar +ivy--file-search-all-files-p nil) - -(defun +ivy--file-search (engine beg end query &optional directory) - (let* ((project-root (doom-project-root)) - (directory (or directory project-root)) - (recursion-p +ivy--file-search-recursion-p) - (all-files-p +ivy--file-search-all-files-p) - (engine (or engine - (and (executable-find "rg") 'rg) - (and (executable-find "ag") 'ag))) - (query - (or query - (if (evil-visual-state-p) - (and beg end - (> (abs (- end beg)) 1) - (rxt-quote-pcre (buffer-substring-no-properties beg end))) - +ivy--file-last-search) - +ivy--file-last-search)) - (prompt - (format "%s%%s %s" - (symbol-name engine) - (cond ((equal directory default-directory) - "./") - ((equal directory project-root) - (projectile-project-name)) - (t - (file-relative-name directory project-root))))) - (default-directory directory)) - (setq +ivy--file-last-search query) - (require 'counsel) - (cl-letf (((symbol-function 'counsel-ag-function) - (symbol-function '+ivy*counsel-ag-function)) - ((symbol-function 'counsel-git-grep-function) - (symbol-function '+ivy*counsel-git-grep-function))) - (pcase engine - ('grep - (let ((args (if recursion-p " -r")) - (counsel-projectile-grep-initial-input query) - (default-directory directory)) - (if all-files-p - (cl-letf (((symbol-function #'projectile-ignored-directories-rel) - (symbol-function #'ignore)) - ((symbol-function #'projectile-ignored-files-rel) - (symbol-function #'ignore))) - (counsel-projectile-grep args)) - (counsel-projectile-grep args)))) - ('ag - (let ((args (concat " -S" ; smart-case - (if all-files-p " -a") - (unless recursion-p " --depth 1")))) - (counsel-ag query directory args (format prompt args)))) - ('rg - (let ((args (concat (if all-files-p " -uu") - (unless recursion-p " --maxdepth 1")))) - (counsel-rg query directory args (format prompt args)))) - ('pt - (let ((counsel-pt-base-command - (concat counsel-pt-base-command - " -S" ; smart-case - (if all-files-p " -U") - (unless recursion-p " --depth=1"))) - (default-directory directory)) - (counsel-pt query))) - (_ (error "No search engine specified")))))) - ;;;###autoload (autoload '+ivy:pt "completion/ivy/autoload/evil" nil t) -(evil-define-operator +ivy:pt (beg end query &optional all-files-p directory) - "Perform a project file search using the platinum searcher. QUERY is a grep -regexp. If omitted, the current selection is used. If no selection is active, -the last known search is used. - -If ALL-FILES-P, don't respect .gitignore files and search everything." - (interactive "") - (let ((+ivy--file-search-all-files-p all-files-p)) - (+ivy--file-search 'pt beg end query directory))) +(evil-define-command +ivy:pt (all-files-p query &optional directory) + "Ex interface for `+ivy/pt'" + (interactive "") + (+ivy/pt all-files-p query directory)) ;;;###autoload (autoload '+ivy:grep "completion/ivy/autoload/evil" nil t) -(evil-define-operator +ivy:grep (beg end query &optional all-files-p directory) - "Perform a project file search using grep (or git-grep in git repos). QUERY is -a grep regexp. If omitted, the current selection is used. If no selection is -active, the last known search is used. - -If ALL-FILES-P, don't respect .gitignore files and search everything." - (interactive "") - (let ((+ivy--file-search-all-files-p all-files-p)) - (+ivy--file-search 'grep beg end query directory))) +(evil-define-command +ivy:grep (all-files-p query &optional directory) + "Ex interface for `+ivy/grep'" + (interactive "") + (+ivy/grep all-files-p query directory)) ;;;###autoload (autoload '+ivy:ag "completion/ivy/autoload/evil" nil t) -(evil-define-operator +ivy:ag (beg end query &optional all-files-p directory) - "Perform a project file search using the silver searcher. QUERY is a pcre -regexp. If omitted, the current selection is used. If no selection is active, -the last known search is used. - -If ALL-FILES-P, don't respect .gitignore files and search everything." - (interactive "") - (let ((+ivy--file-search-all-files-p all-files-p)) - (+ivy--file-search 'ag beg end query directory))) +(evil-define-command +ivy:ag (all-files-p query &optional directory) + "Ex interface for `+ivy/ag'" + (interactive "") + (+ivy/ag all-files-p query directory)) ;;;###autoload (autoload '+ivy:rg "completion/ivy/autoload/evil" nil t) -(evil-define-operator +ivy:rg (beg end query &optional all-files-p directory) - "Perform a project file search using ripgrep. QUERY is a regexp. If omitted, -the current selection is used. If no selection is active, the last known search -is used. +(evil-define-command +ivy:rg (all-files-p query &optional directory) + "Ex interface for `+ivy/rg'" + (interactive "") + (+ivy/rg all-files-p query directory)) -If ALL-FILES-P, don't respect .gitignore files and search everything. -NOTE: ripgrep doesn't support multiline searches (yet)." +;;;###autoload (autoload '+ivy:pt-from-cwd "completion/ivy/autoload/evil" nil t) +(evil-define-command +ivy:pt-from-cwd (query &optional bang) + "Ex interface for `+ivy/pt-from-cwd'." (interactive "") - (let ((+ivy--file-search-all-files-p all-files-p)) - (+ivy--file-search 'rg beg end query directory))) + (+ivy/pt-from-cwd (not bang) query)) - -;;;###autoload (autoload '+ivy:pt-cwd "completion/ivy/autoload/evil" nil t) -(evil-define-operator +ivy:pt-cwd (beg end query &optional bang) - "The same as :grep, but searches the current directory. If BANG, don't recurse -into sub-directories." +;;;###autoload (autoload '+ivy:grep-from-cwd "completion/ivy/autoload/evil" nil t) +(evil-define-command +ivy:grep-from-cwd (query &optional bang) + "Ex interface for `+ivy/grep-from-cwd'." (interactive "") - (let ((+ivy--file-search-recursion-p (not bang))) - (+ivy:pt beg end query t default-directory))) + (+ivy/grep-from-cwd (not bang) query)) -;;;###autoload (autoload '+ivy:grep-cwd "completion/ivy/autoload/evil" nil t) -(evil-define-operator +ivy:grep-cwd (beg end query &optional bang) - "The same as :grep, but searches the current directory. If BANG, don't recurse -into sub-directories." +;;;###autoload (autoload '+ivy:ag-from-cwd "completion/ivy/autoload/evil" nil t) +(evil-define-command +ivy:ag-from-cwd (query &optional bang) + "Ex interface for `+ivy/ag-from-cwd'." (interactive "") - (let ((+ivy--file-search-recursion-p (not bang))) - (+ivy:grep beg end query t default-directory))) + (+ivy/ag-from-cwd (not bang) query)) -;;;###autoload (autoload '+ivy:ag-cwd "completion/ivy/autoload/evil" nil t) -(evil-define-operator +ivy:ag-cwd (beg end query &optional bang) - "The same as :ag, but searches the current directory. If BANG, don't recurse -into sub-directories." +;;;###autoload (autoload '+ivy:rg-from-cwd "completion/ivy/autoload/evil" nil t) +(evil-define-command +ivy:rg-from-cwd (query &optional bang) + "Ex interface for `+ivy/rg-from-cwd'." (interactive "") - (let ((+ivy--file-search-recursion-p (not bang))) - (+ivy:ag beg end query t default-directory))) + (+ivy/rg-from-cwd (not bang) query)) -;;;###autoload (autoload '+ivy:rg-cwd "completion/ivy/autoload/evil" nil t) -(evil-define-operator +ivy:rg-cwd (beg end query &optional bang) - "The same as :rg, but only searches the current directory. If BANG, don't -recurse into sub-directories. - -NOTE: ripgrep doesn't support multiline searches (yet)." - (interactive "") - (let ((+ivy--file-search-recursion-p (not bang))) - (+ivy:rg beg end query t default-directory))) - - -;; -;; Advice -;; - -;;;###autoload -(defun +ivy*counsel-ag-function (string) - "Advice to get rid of the character limit from `counsel-ag-function'. - -NOTE This may need to be updated frequently, to meet changes upstream (in -counsel-rg)." - (if (< (length string) 1) ; <-- modified the character limit - (counsel-more-chars 1) ; <-- - (let ((default-directory (ivy-state-directory ivy-last)) - (regex (counsel-unquote-regex-parens - (setq ivy--old-re - (ivy--regex string))))) - (counsel--async-command (format counsel-ag-command - (shell-quote-argument regex))) - nil))) - -;;;###autoload -(defun +ivy*counsel-git-grep-function (string) - "Advice to get rid of the character limit from `counsel-git-grep-function'. - -NOTE This may need to be updated frequently, to meet changes upstream (in -counsel-git-grep)." - (if (and (> counsel--git-grep-count counsel--git-grep-count-threshold) - (< (length string) 1)) ; <-- modified the character limit - (counsel-more-chars 1) ; <-- - (let* ((default-directory (ivy-state-directory ivy-last)) - (cmd (format counsel-git-grep-cmd - (setq ivy--old-re (ivy--regex string t))))) - (if (<= counsel--git-grep-count counsel--git-grep-count-threshold) - (split-string (shell-command-to-string cmd) "\n" t) - (counsel--gg-candidates (ivy--regex string)) - nil)))) diff --git a/modules/completion/ivy/autoload/ivy.el b/modules/completion/ivy/autoload/ivy.el index b5caaeb36..251b5b65e 100644 --- a/modules/completion/ivy/autoload/ivy.el +++ b/modules/completion/ivy/autoload/ivy.el @@ -196,3 +196,205 @@ search current file. See `+ivy-task-tags' to customize what this searches for." (re-search-forward (ivy--regex ivy-text t) (line-end-position) t) (run-hooks 'counsel-grep-post-action-hook) (selected-window)))))) + + +;; +;; File searching +;; + +(defvar +ivy--file-last-search nil) +(defvar +ivy--file-search-recursion-p t) +(defvar +ivy--file-search-all-files-p nil) + +(defun +ivy--file-search (engine &optional query directory) + (let* ((project-root (doom-project-root)) + (directory (or directory project-root)) + (recursion-p +ivy--file-search-recursion-p) + (all-files-p +ivy--file-search-all-files-p) + (engine (or engine + (and (executable-find "rg") 'rg) + (and (executable-find "ag") 'ag))) + (query + (or query + (when (use-region-p) + (let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning))) + (end (or (bound-and-true-p evil-visual-end) (region-end)))) + (when (> (abs (- end beg)) 1) + (rxt-quote-pcre (buffer-substring-no-properties beg end))))) + +ivy--file-last-search)) + (prompt + (format "%s%%s %s" + (symbol-name engine) + (cond ((equal directory default-directory) + "./") + ((equal directory project-root) + (projectile-project-name)) + (t + (file-relative-name directory project-root))))) + (default-directory directory)) + (setq +ivy--file-last-search query) + (require 'counsel) + (cl-letf (((symbol-function 'counsel-ag-function) + (symbol-function '+ivy*counsel-ag-function)) + ((symbol-function 'counsel-git-grep-function) + (symbol-function '+ivy*counsel-git-grep-function))) + (pcase engine + ('grep + (let ((args (if recursion-p " -r")) + (counsel-projectile-grep-initial-input query) + (default-directory directory)) + (if all-files-p + (cl-letf (((symbol-function #'projectile-ignored-directories-rel) + (symbol-function #'ignore)) + ((symbol-function #'projectile-ignored-files-rel) + (symbol-function #'ignore))) + (counsel-projectile-grep args)) + (counsel-projectile-grep args)))) + ('ag + (let ((args (concat " -S" ; smart-case + (if all-files-p " -a") + (unless recursion-p " --depth 1")))) + (counsel-ag query directory args (format prompt args)))) + ('rg + (let ((args (concat (if all-files-p " -uu") + (unless recursion-p " --maxdepth 1")))) + (counsel-rg query directory args (format prompt args)))) + ('pt + (let ((counsel-pt-base-command + (concat counsel-pt-base-command + " -S" ; smart-case + (if all-files-p " -U") + (unless recursion-p " --depth=1"))) + (default-directory directory)) + (counsel-pt query))) + (_ (error "No search engine specified")))))) + +;;;###autoload +(defun +ivy/project-search (arg) + "Performs a project search using the first available search backend from a +list of: ripgrep, ag, pt, git-grep and grep. If ARG (universal argument), +preform search from current directory." + (interactive "P") + (call-interactively + (cond ((executable-find "rg") (if arg #'+ivy/rg-from-cwd #'+ivy/rg)) + ((executable-find "ag") (if arg #'+ivy/ag-from-cwd #'+ivy/ag)) + ((executable-find "pt") (if arg #'+ivy/pt-from-cwd #'+ivy/pt)) + (arg #'+ivy/grep-from-cwd) + (#'+ivy/grep)))) + +;;;###autoload +(defun +ivy/rg (all-files-p &optional query directory) + "Perform a project file search using ripgrep. QUERY is a regexp. If omitted, +the current selection is used. If no selection is active, the last known search +is used. + +If ALL-FILES-P, don't respect .gitignore files and search everything. + +NOTE: ripgrep doesn't support multiline searches (yet)." + (interactive "P") + (let ((+ivy--file-search-all-files-p all-files-p)) + (+ivy--file-search 'rg (or query "") directory))) + +;;;###autoload +(defun +ivy/ag (all-files-p &optional query directory) + "Perform a project file search using the silver searcher. QUERY is a pcre +regexp. If omitted, the current selection is used. If no selection is active, +the last known search is used. + +If ALL-FILES-P, don't respect .gitignore files and search everything." + (interactive) + (let ((+ivy--file-search-all-files-p all-files-p)) + (+ivy--file-search 'ag (or query "") directory))) + +;;;###autoload +(defun +ivy/pt (all-files-p &optional query directory) + "Perform a project file search using the platinum searcher. QUERY is a grep +regexp. If omitted, the current selection is used. If no selection is active, +the last known search is used. + +If ALL-FILES-P, don't respect .gitignore files and search everything." + (interactive) + (let ((+ivy--file-search-all-files-p all-files-p)) + (+ivy--file-search 'pt (or query "") directory))) + +;;;###autoload +(defun +ivy/grep (all-files-p &optional query directory) + "Perform a project file search using grep (or git-grep in git repos). QUERY is +a grep regexp. If omitted, the current selection is used. If no selection is +active, the last known search is used. + +If ALL-FILES-P, don't respect .gitignore files and search everything." + (interactive) + (let ((+ivy--file-search-all-files-p all-files-p)) + (+ivy--file-search 'grep (or query "") directory))) + + +;;;###autoload +(defun +ivy/rg-from-cwd (recursive-p &optional query) + "Like `+ivy/rg', but from the current directory (recursively if RECURSIVE-P is +non-nil)." + (interactive "P") + (let ((+ivy--file-search-recursion-p recursive-p)) + (+ivy/rg t query default-directory))) + +;;;###autoload +(defun +ivy/ag-from-cwd (recursive-p &optional query) + "Like `+ivy/ag', but from the current directory (recursively if RECURSIVE-P is +non-nil)." + (interactive "P") + (let ((+ivy--file-search-recursion-p recursive-p)) + (+ivy/ag t query default-directory))) + +;;;###autoload +(defun +ivy/pt-from-cwd (recursive-p &optional query) + "Like `+ivy/pt', but from the current directory (recursively if RECURSIVE-P is +non-nil)." + (interactive "P") + (let ((+ivy--file-search-recursion-p recursive-p)) + (+ivy/pt t query default-directory))) + +;;;###autoload +(defun +ivy/grep-from-cwd (recursive-p &optional query) + "Like `+ivy/grep', but from the current directory (recursively if RECURSIVE-P is +non-nil)." + (interactive "P") + (let ((+ivy--file-search-recursion-p recursive-p)) + (+ivy/grep t query default-directory))) + + +;; +;; Advice +;; + +;;;###autoload +(defun +ivy*counsel-ag-function (string) + "Advice to get rid of the character limit from `counsel-ag-function'. + +NOTE This may need to be updated frequently, to meet changes upstream (in +counsel-rg)." + (if (< (length string) 1) ; <-- modified the character limit + (counsel-more-chars 1) ; <-- + (let ((default-directory (ivy-state-directory ivy-last)) + (regex (counsel-unquote-regex-parens + (setq ivy--old-re + (ivy--regex string))))) + (counsel--async-command (format counsel-ag-command + (shell-quote-argument regex))) + nil))) + +;;;###autoload +(defun +ivy*counsel-git-grep-function (string) + "Advice to get rid of the character limit from `counsel-git-grep-function'. + +NOTE This may need to be updated frequently, to meet changes upstream (in +counsel-git-grep)." + (if (and (> counsel--git-grep-count counsel--git-grep-count-threshold) + (< (length string) 1)) ; <-- modified the character limit + (counsel-more-chars 1) ; <-- + (let* ((default-directory (ivy-state-directory ivy-last)) + (cmd (format counsel-git-grep-cmd + (setq ivy--old-re (ivy--regex string t))))) + (if (<= counsel--git-grep-count counsel--git-grep-count-threshold) + (split-string (shell-command-to-string cmd) "\n" t) + (counsel--gg-candidates (ivy--regex string)) + nil)))) diff --git a/modules/config/default/+bindings.el b/modules/config/default/+bindings.el index 9b470cf54..31eb4c0e0 100644 --- a/modules/config/default/+bindings.el +++ b/modules/config/default/+bindings.el @@ -116,10 +116,12 @@ :desc "Spelling correction" :n "S" #'flyspell-correct-word-generic) (:desc "search" :prefix "/" - :desc "Swiper" :nv "/" #'swiper - :desc "Imenu" :nv "i" #'imenu - :desc "Imenu across buffers" :nv "I" #'imenu-anywhere - :desc "Online providers" :nv "o" #'+lookup/online-select) + :desc "Project" :nv "p" #'+ivy/project-search + :desc "Directory" :nv "d" (λ! (+ivy/project-search t)) + :desc "Buffer" :nv "b" #'swiper + :desc "Symbols" :nv "i" #'imenu + :desc "Symbols across buffers" :nv "I" #'imenu-anywhere + :desc "Online providers" :nv "o" #'+lookup/online-select) (:desc "workspace" :prefix "TAB" :desc "Display tab bar" :n "TAB" #'+workspace/display diff --git a/modules/config/default/+evil-commands.el b/modules/config/default/+evil-commands.el index 6105989a8..619246aba 100644 --- a/modules/config/default/+evil-commands.el +++ b/modules/config/default/+evil-commands.el @@ -82,11 +82,13 @@ (ex! "pwd" #'doom:pwd) (cond ((featurep! :completion ivy) (ex! "ag" #'+ivy:ag) - (ex! "agc[wd]" #'+ivy:ag-cwd) + (ex! "agc[wd]" #'+ivy:ag-from-cwd) (ex! "rg" #'+ivy:rg) - (ex! "rgc[wd]" #'+ivy:rg-cwd) + (ex! "rgc[wd]" #'+ivy:rg-from-cwd) + (ex! "pt" #'+ivy:pt) + (ex! "ptc[wd]" #'+ivy:pt-from-cwd) (ex! "grep" #'+ivy:grep) - (ex! "grepc[wd]" #'+ivy:grep-cwd) + (ex! "grepc[wd]" #'+ivy:grep-from-cwd) (ex! "sw[iper]" #'+ivy:swiper) (ex! "todo" #'+ivy:todo)) ((featurep! :completion helm)