minad / consult

:mag: consult.el - Consulting completing-read
GNU General Public License v3.0
1.14k stars 99 forks source link

Attempting to include 'ugrep' - code feedback #604

Closed joachimnielandt closed 1 year ago

joachimnielandt commented 1 year ago

Hello all,

This is not exactly a consult issue, more of a programming-support question that could eventually lead to a contribution. So, I hope I'm at the right place :). If not, feel free to close this issue.

I'm attempting to include ugrep as a grep alternative, as I'm interested in using it for its --bool --files flags to search multiple keywords file-wide. As one of my first attempts at elisp, I was going off the consult--grep-* functions to see if I could make sense of this. I'm evaluating the MVE code below in a buffer, then running consult-ugrep and passing some #search text to it.

(setq consult-ugrep-args "ugrep --null --line-buffered --color=never --ignore-case --bool --files --exclude-dir=.git --line-number -I -r")

(defun consult--ugrep-builder (input)
  "See consult--grep-builder."
  (pcase-let* ((cmd (split-string-and-unquote consult-ugrep-args))
               (type (consult--grep-regexp-type (car cmd)))
               (`(,arg . ,opts) (consult--command-split input))
               (`(,re . ,hl) (funcall consult--regexp-compiler arg type
                                      (member "--ignore-case" cmd))))
    (let ((command (append cmd
                           (list (concat "'" input "'"))
                           (list ".")
                           opts)))
      (when re
        (list :command command
              :highlight hl)))))

(defun consult--ugrep (prompt builder dir initial)
  "See consult--grep."
  (let* ((prompt-dir (consult--directory-prompt prompt dir))
         (initial "test")
         (default-directory (cdr prompt-dir)))
    (consult--read
     (consult--async-command builder
       (consult--grep-format builder)
       :file-handler t) ;; allow tramp
     :prompt (car prompt-dir)
     :lookup #'consult--lookup-member
     :state (consult--grep-state)
     :initial (consult--async-split-initial initial)
     :add-history (consult--async-split-thingatpt 'symbol)
     :require-match t
     :category 'consult-grep
     :group #'consult--grep-group
     :history '(:input consult--grep-history)
     :sort nil)))

(defun consult-ugrep (&optional dir initial)
  "See consult-grep."
  (interactive "P")
  (consult--ugrep "ugrep" #'consult--ugrep-builder dir initial))

In the *consult-async* buffer I'm getting this error:

consult--async-process started ("ugrep" "--null" "--line-buffered" "--color=never" "--ignore-case" "--bool" "--files" "--exclude-dir=.git" "--line-number" "-I" "-r" "'test'" ".")
 > consult--async-process name "ugrep"
consult--async-process sentinel: event=exited abnormally with code 1 lines=0
>>>>> stderr >>>>>
<<<<< stderr <<<<<

Executing this command myself in the project folder (ugrep --null --line-buffered ...) is functional, emacs has ugrep on its PATH. As stderr output is empty in the error buffer I'm not sure how to proceed here.

I'm running the windows client, and tested under WSL(ubuntu) with similar results.

minad commented 1 year ago

I am not sure what causes the abnormal exit. Either the command is not found or the command exits immediately with error code 1. consult-grep and consult-ripgrep work for you? If these don't work you should probably investigate these commands first. I am not sure if a separate consult-ugrep is really needed. Maybe it suffices to just adjust consult-grep-args globally or via let?

(defun consult-ugrep ()
  (interactive)
  (let ((consult-grep-args "ugrep ..."))
     (call-interactively #'consult-grep)))

If a separate command (and a separate command builder function) is needed due to special ugrep features, I would appreciate if you create a separate package in the style of consult-ag. I don't want to add support for ugrep to Consult directly, since there are many flavors of grep commands, such that things would get out of hand. Thanks!

joachimnielandt commented 1 year ago

Nice to see how you could wrap consult-grep in a separate function. In this case though, I think I'd need a different command builder as ugrep (for my usecase anyway) would be called this way:

ugrep --bool 'a b c'

This would give all files that have a, b and c somewhere in the file. Passing multiple words to the current consult-grep builder function translates into a regex I believe.

I will attempt to create a package similar to consult-ag, thanks for the suggestions!

joachimnielandt commented 1 year ago

@minad

Consult-ag could be easily modified into using ugrep apparently. I made an attempt to modify the underlying consult-ag functions (with call-interactively, as you mentioned) but couldn't quite get it right... have to study some more elisp I guess. I created a fork of consult-ag instead, which seems to do the trick.

https://github.com/joachimnielandt/consult-ugrep

manuel-uberti commented 9 months ago

FWIW, I gave it a shot by following consult-git-grep and consult-ripgrep approach: https://manueluberti.eu/2023/09/16/consult-ugrep.html

NightMachinery commented 3 months ago

@manuel-uberti That link is 404.

manuel-uberti commented 3 months ago

Yep, that's because in the meantime I reworked my website. Here it is: https://manueluberti.eu/posts/2023-09-16-consult-ugrep/

NightMachinery commented 3 months ago

@manuel-uberti Thanks. I forked your solution to provide a ugrep --bool experience. If anyone's interested, the code is here.