Jak rekurencyjnie przeszukiwać / grep zawartość pliku w katalogu / podkatalogach w Emacsie?

14

Używam Emacsa od dłuższego czasu i nadal jakoś nie rozumiem, jaki jest najlepszy sposób (rekurencyjnie) grep dla ciągu w katalogu (projektu) i otrzymuję wyniki przedstawione jako bufor diodowy lub w jeszcze więcej użyteczny sposób. Proszę, oświeć mnie.

Boris Stitnicky
źródło
Czy próbowałeś użyć helm-projectile-greppolecenia (jeśli masz zainstalowany pocisk steru) lub projectile-grep?
Chakravarthy Raghunandan
Nie wiem, co jest helmlub projectilejest, ale jeśli jest to zalecane narzędzie, zainstalowałbym je i nauczę się.
Boris Stitnicky
Tak, pocisk to pakiet, który zapewnia łatwy sposób wykonywania tego, co wspomniano w pytaniu za pomocą polecenia projectile-grep. RÓWNIEŻ polecam zainstalować helmpakiet. Nie wyobrażam sobie uruchamiania emacsa bez helmi helm-projectile. Możesz być także interesujący w helm-agpakiecie (który zapewnia interfejs sterujący dla srebrnego wyszukiwarki w emacsie, który jest podobno szybszy niż grep lub ack).
Chakravarthy Raghunandan
2
Aby pytanie było bardziej przydatne i łatwiejsze do znalezienia dla przyszłych wyszukiwarek Google i forum, być może rozważ modyfikację tytułu pytania, aby zawierało ono rekursywnie , i rozważ ograniczenie zakresu - np. Jak rekurencyjnie grepować zawartość pliku w katalogu / podkatalogi . To tylko przykład - zamiast tego może być coś innego.
prawnik
1
Chociaż dotychczasowe komentarze i odpowiedzi sugerowały szereg pakietów, aby to zrobić na różne fantazyjne sposoby, nie widzę w twoim pytaniu, dlaczego proste M-x grepnie robi tego, czego potrzebujesz. To pozwala ci podać dowolną linię poleceń, której czasami używam find, gdy potrzebuję rekurencji.
MAP

Odpowiedzi:

15

Cóż, ponieważ oryginalne pytanie nie wspomina rgrep, postaram się to wyjaśnić tutaj. Jest to najprostsza opcja już wbudowana w Emacsa i okazało się, że jest wystarczająca do większości rzeczy.

Z dokumentacji

Recursively grep for REGEXP in FILES in directory tree rooted at DIR.

Wyszukiwanie jest ograniczone do nazw plików pasujących do wzorca powłoki PLIKI.

Tak więc, na przykład, aby wyszukać wszystkie pliki Pythona pod moim katalogu domowym dla zastosowań some_function, nazwałbym go some_function, *.py, ~/jako argumenty. Wyniki są wyświetlane w nowym buforze.

użytkownik2699
źródło
1
Mógłbym również chcieć wspomnieć, find-grep-diredponieważ OP wspomniał o „wynikach przedstawionych jako bufor dired lub ...”
npostavs
@npostavs Nie korzystałem z find-grep-diredsiebie, dodaj to do odpowiedzi.
user2699,
Kiedyś eksperymentowałem z takimi jak ack i ag, przy założeniu, że powinienem przejść z rgrep na coś przy użyciu tych, ponieważ miały być lepsze. W końcu pozostałem przy rgrep, ponieważ nie znalazłem żadnej korzyści ze zmiany! W wierszu poleceń z pewnością jest uzasadnienie, ale w Emacs rgreprobi tak dobrą robotę, kierując wyszukiwanie, że nie było między nimi znaczących różnic prędkości. (Chcę powiedzieć, że rgrep był w niektórych przypadkach frakcjonalnie szybszy , ale nie pamiętam tego na pewno.)
Phils
1
Zauważ, że next-errori previous-errormoże służyć do poruszania się po zestawie wyników. Znajduję M-g M-ni M-g M-pnajwygodniejszy ze standardowych wiązań.
phils
find-grep-dirednie daje mi bufora z linkami odsyłającymi. rgreprobi to dla mnie i ta ostatnia sugestia next-error previous-errorjest fantastyczna! Moim jedynym sporem jest to, że wszystkie niechlujne dane wyjściowe wykonywania komend na początku bufora.
Robert
11

Z przyjemnością korzystam M-x find-grep-diredod lat. Zwraca wyniki w postaci buforowanego bufora, którego można użyć.

Michael Albinus
źródło
Miałem pewne trudności z uzyskaniem linku do odsyłaczy, który mógłby z tym pracować. Jego konfiguracja jest dość paskudna ( find-ls-option), a w końcu dostajesz coś bardzo gadatliwego, ponieważ nie działa bez daty przed nazwą pliku!
Robert
5

Rozwiązanie 1 (najlepsze rozwiązanie):

Zainstaluj doradcę ( https://github.com/abo-abo/swiper/blob/master/counsel.el )

Potem M-x counsel-git-grep.

Nie jest wymagana konfiguracja (git zna katalog główny projektu i pliki do wykluczenia). Zarówno git grepi counseljest wydajny.

Projekt musi być zarządzany przez git.

rada wymaga trybu bluszczu.

Rozwiązanie 2:

To rozwiązanie używa grep i działa na każdym projekcie. Jest gorszy od rozwiązania 1, ponieważ jest wolniejszy i wymaga ręcznej konfiguracji. Opiera się również na trybie bluszczu.

(defvar simple-project-root "~/.emacs.d")
(defun simple-grep ()
  (interactive)
  (unless (featurep 'ivy)
    (require 'ivy))
  (let* ((default-directory (file-name-as-directory simple-project-root))
         (keyword (read-string "Keyword:")))
    (ivy-read (format "Grep at %s:" simple-project-root)
              (split-string (shell-command-to-string (format "grep -rsnI %s ." keyword)) "\n" t)
              :action (lambda (val)
                        (let* ((lst (split-string val ":"))
                               (linenum (string-to-number (cadr lst))))
                          ;; open file
                          (find-file (car lst))
                          ;; goto line if line number exists
                          (when (and linenum (> linenum 0))
                            (goto-char (point-min))
                            (forward-line (1- linenum))))))))

Aby skonfigurować simple-project-root, musisz utworzyć plik .dir-locals.el , zobacz https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html w celu uzyskania szczegółowych informacji technicznych

Kod w rozwiązaniu 2 jest tylko prototypem. Moja prawdziwa implementacja jest znacznie bardziej złożona. Zobacz counsel-etags-grepw https://github.com/redguardtoo/counsel-etags/blob/master/counsel-etags.el

Streszczenie:

To dwa najlepsze rozwiązania, jakie znam.

Jeśli istnieją jakieś inne lepsze rozwiązania, muszą one przynajmniej rozwiązać poniższe problemy, aby były gotowe do produkcji,

  • jak zmusić słowo kluczowe do grep (na przykład uzyskać słowo kluczowe z wybranego regionu)

  • uciec od słowa kluczowego

  • jeśli istnieje bardziej wydajny program grep, powinniśmy go użyć (ripgrep, the_silver_searcher / ag, ... itd.), lub cofnąć domyślny grep

  • okno kandydata powinno korzystać z pełnej szerokości ekranu, a użytkownicy mogą interaktywnie filtrować kandydatów (dlatego ludzie używają bluszczu lub hełmu)

  • powinniśmy pokazać ścieżkę względną w oknie kandydata

  • w stanie ponownie użyć poprzedniego grepowanego wyniku. Poprzedni wynik powinien zostać zapisany. Możesz używać ivy-resumez ivylub helm-resumezhelm

  • Kiedy zapisujesz poprzedni grepowany wynik, kontekst poprzedniego wyniku również powinien zostać zapisany. Na przykład w kodzie rozwiązania 2. default-directoryjest kontekst. Więcej informacji na stronie https://github.com/abo-abo/swiper/issues/591 .

  • Należy użyć rozszerzonego wyrażenia regularnego, ponieważ jest ono prostsze i jest już używane przez counsel-git-grepthe_silver_searcher / ag.

Chen Bin
źródło
4

Chciałbym dodać kolejne rozwiązanie do rozwiązania @chen bin, które również wykorzystuje counsel(ale nie musisz mieć projektu gitdo tego utrzymanego ).

Musisz zainstalować ag (srebrny wyszukiwarka) i counselpodać polecenie, counsel-agktóre przeszukuje bieżący katalog pod kątem wprowadzonego ciągu.

Jeśli chcesz wyszukiwać w projekcie (nie tylko w bieżącym katalogu roboczym), dodaj to do .emacs:

  (defun rag/counsel-ag-project-at-point ()
    "use counsel ag to search for the word at point in the project"
    (interactive)
    (counsel-ag (thing-at-point 'symbol) (projectile-project-root)))

Ta funkcja posłuży agdo rekurencyjnego przeszukiwania katalogu głównego projektu.

Edycja : powyższa funkcja domyślnie przyjmuje symbol w punkcie wyszukiwania. Jeśli nie lubisz tego zachowania, należy wymienić (thing-at-point 'symbol)z nil. A jeśli chcesz wyniki wyszukiwania we własnym buforze naciśnijC-c C-o

Edit2 : jeśli już ripgrepzainstalowany, można wymienić counsel-agze counsel-rgw powyższej funkcji, a to będzie tak jak grzywny :)

Chakravarthy Raghunandan
źródło
Czy projectile-project-rootmożna znaleźć katalog główny projektu Rails?
Boris Stitnicky,
Tak, dopóki będzie to prawidłowy projekt pocisku, będzie działać. Myślę, że był jakiś pakiet zwany szynami pocisków.
Chakravarthy Raghunandan
Dlaczego nazwałeś funkcję rag/...?
Boris Stitnicky,
Jest to powszechny styl, którego ludzie często używają podczas pisania własnych funkcji (możesz zobaczyć, że robi to wiele innych osób, jeśli przeglądasz ich konfigurację emacs). Wszystkie funkcje, które zdefiniowałem, zaczynają się od rag/przedrostka, dzięki czemu można je łatwo znaleźć pod M-x:)
Chakravarthy Raghunandan,
Ic, to twoje imię :-)
Boris Stitnicky,
2

Oto przykład niestandardowej funkcji grep, która rekursywnie grepuje zawartość pliku (używając wyrażenia regularnego dla wyszukiwanego terminu) w katalogu i jego podkatalogach oraz wyświetla wyniki z liniami kontekstu przed i po. Wbudowane compile.eli grep.elbiblioteki zapewniają kilka metod przeskakiwania do lokalizacji w pliku zawierającym wyniki wyszukiwania. Aby ten przykład działał, nie trzeba instalować niczego innego - potrzebna jest tylko pełna wersja Emacsa i komputer z grepzainstalowanym narzędziem. Zmienną grep-programmożna dostosować tak, aby wskazywała określoną lokalizację grepnarzędzia, jeśli wartość domyślna nie jest wystarczająca.

(defun recursive-grep ()
"Recursively grep file contents.  `i` case insensitive; `n` print line number;
`I` ignore binary files; `E` extended regular expressions; `r` recursive"
(interactive)
  (let* ((search-term (read-string "regex:  "))
         (search-path
           (directory-file-name (expand-file-name (read-directory-name "directory:  "))))
         (default-directory (file-name-as-directory search-path))
         (grep-command
           (concat
             grep-program
             " "
             "-inIEr --color=always -C2"
             " "
             search-term
             " "
             search-path)))
    (compilation-start grep-command 'grep-mode (lambda (mode) "*grep*") nil)))
lista prawnicza
źródło
Chociaż pytanie nie dotyczy sposobu jednoczesnej modyfikacji wielu plików, które są zwracane jako wyniki przez powyższą funkcję grep, uważam, że jest to bardzo pomocne w użyciu multiple-cursorsi wgrep. Sprawia, że ​​modyfikowanie wielu plików za jednym zamachem jest dziecinnie proste. Wymienione biblioteki będą jednak musiały zostać zainstalowane, jeśli te funkcje są pożądane.
prawnik
Jaka jest zaleta korzystania z tego nad wbudowanym rgrep?
npostavs
1
@npostavs - Uważam, że wbudowanie rgrepjest czasochłonne w dostosowywaniu do moich preferowanych kryteriów wyszukiwania wyrażeń regularnych i zdecydowałem się na sztywne kodowanie wszystkiego w funkcji niestandardowej (której używam kilka razy dziennie). Jeśli chcesz napisać alternatywną odpowiedź, która opisuje sposób dostosowywania rgrep, byłoby to pomocne dla innych czytelników forum w przyszłości.
prawnik