Como pesquisar recursivamente o conteúdo do arquivo grep em um diretório / subdiretórios no Emacs?

14

Estou usando o Emacs há algum tempo e ainda assim, de alguma forma, não consigo entender qual é a melhor maneira de (recursivamente) grep para uma string em um diretório (project) e obter os resultados apresentados como um buffer direcionado ou em alguns outros maneira útil. Por favor me esclareça.

Boris Stitnicky
fonte
Você já tentou usar o helm-projectile-grepcomando (se você tem o projétil de leme instalado) ou projectile-grep?
Chakravarthy Raghunandan
Não sei o que é helmou projectileé, mas, se for uma ferramenta recomendada, eu instalaria e aprenderia.
Boris Stitnicky
Sim, projéctil é um pacote que fornece uma maneira fácil de fazer o que você mencionado na pergunta com o comando projectile-grep. Além disso, eu recomendo que você instale o helmpacote. Não consigo imaginar rodar o emacs sem helme helm-projectile. Você também pode ser interessante no helm-agpacote (que fornece uma interface de leme para o pesquisador prateado no emacs, que é supostamente mais rápido que o grep ou o ack).
Chakravarthy Raghunandan
2
Para que a pergunta seja mais útil e fácil de encontrar para futuros pesquisadores do Google e de fóruns, talvez considere modificar o título da pergunta para incluir a palavra recursivamente e considere limitar o escopo - por exemplo, como grep recursivamente o conteúdo do arquivo em um diretório / subdiretórios . Isso é apenas um exemplo - poderia ser outra coisa.
lawlist
1
Embora os comentários e respostas até agora tenham sugerido vários pacotes para fazer isso de várias maneiras sofisticadas, não vejo na sua pergunta por que o simples M-x grepnão faz o que você precisa. Vamos dar uma linha de comando arbitrária, às vezes eu uso findlá quando preciso da recursão.
MAP

Respostas:

15

Bem, como a pergunta original não menciona rgrep, vou adiante e aponto aqui. Esta é a opção mais simples já incorporada ao Emacs, e eu achei que era mais do que suficiente para a maioria das coisas.

A partir da documentação,

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

A pesquisa é limitada a nomes de arquivos que correspondem ao padrão de shell FILES.

Assim, por exemplo, para pesquisar todos os arquivos python sob o meu diretório home para usos de some_function, gostaria de chamá-lo com some_function, *.py, ~/como os argumentos. Os resultados são exibidos em um novo buffer.

user2699
fonte
1
Pode querer mencionado find-grep-diredtambém desde OP mencionou "os resultados apresentados como um buffer dired ou ..."
npostavs
@npostavs Eu não find-grep-diredme usei , fique à vontade para adicioná-lo à resposta.
User2699
Certa vez, experimentei ack e ag, partindo do pressuposto de que eu deveria passar do rgrep para algo que os utilizasse, porque eles deveriam ser melhores. No final, fiquei com o rgrep, porque não achei benefício em mudar! Na linha de comando, certamente há um argumento a ser feito, mas o Emacs rgrepfaz um trabalho tão bom em direcionar a pesquisa que não houve diferenças significativas de velocidade entre elas. (Eu quero dizer que rgrep foi fracionada mais rápido em alguns casos, mas não me lembro com certeza.)
phils
1
Observe que next-errore previous-errorpode ser usado para navegar pelo conjunto de resultados. Acho M-g M-ne M-g M-pa mais conveniente das ligações padrão.
phils
find-grep-dirednão me fornece um buffer com links de referência cruzada. rgrepfaz isso por mim e essa última sugestão next-error previous-erroré fantástica! Minha única reclamação é que toda a execução de comandos confusos é gerada no início do buffer.
robert
11

Eu felizmente uso M-x find-grep-diredpor anos. Ele retorna os resultados como um buffer direcionado, que você pode usar.

Michael Albinus
fonte
Eu tive algumas dificuldades em obter o link de referência cruzada para trabalhar com isso. Sua configuração é bastante desagradável ( find-ls-option) e, afinal, você acaba com algo bastante detalhado, porque não funciona sem ter a data anterior ao nome do arquivo!
robert
5

Solução 1 (melhor solução):

Conselho de instalação ( https://github.com/abo-abo/swiper/blob/master/counsel.el )

Então M-x counsel-git-grep.

Nenhuma configuração é necessária (o git conhece a raiz e os arquivos do projeto a serem excluídos). Ambos git grepe counselé eficiente.

O projeto precisa ser gerenciado pelo git.

conselho exige o modo de hera.

Solução 2:

Esta solução usa grep e funciona em qualquer projeto. É inferior à Solução 1 porque é mais lenta e precisa de configuração manual. Também é baseado no modo ivy.

(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))))))))

Você precisa criar .dir-locals.el para configurar simple-project-root, consulte https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html para obter detalhes técnicos

O código na solução 2 é apenas um protótipo. Minha implementação real é muito mais complexa. Veja counsel-etags-grepem https://github.com/redguardtoo/counsel-etags/blob/master/counsel-etags.el

Resumo:

Essas são as duas melhores soluções que conheço.

Se existirem outras soluções melhores, elas precisam pelo menos resolver os problemas abaixo para estarem prontas para a produção,

  • como obter a palavra-chave grep (por exemplo, obter palavra-chave da região selecionada)

  • escapar da palavra-chave

  • se existir um programa grep mais eficiente, devemos usá-lo (ripgrep, the_silver_searcher / ag, ... etc), ou então usar o grep padrão

  • a janela do candidato deve usar a largura total da tela e os usuários podem filtrar os candidatos interativamente (é por isso que as pessoas usam a hera ou o leme)

  • devemos mostrar o caminho relativo na janela do candidato

  • capaz de reutilizar o resultado grepped anterior. Portanto, o resultado anterior deve ser salvo. Você pode usar ivy-resumede ivyou helm-resumedehelm

  • Quando você salva o resultado anterior recebido, o contexto do resultado anterior também deve ser salvo. Por exemplo, no código da Solução 2. default-directoryé o contexto. Consulte https://github.com/abo-abo/swiper/issues/591 para obter mais detalhes.

  • Expressão regular estendida deve ser usada porque é mais simples e já é usada por counsel-git-grepthe_silver_searcher / ag.

Chen Bin
fonte
4

Gostaria de adicionar outra solução à solução do @chen bin, que também usa counsel(mas você não precisa ter um projeto mantido gitpara isso).

Você precisa instalar o ag (o pesquisador prateado) e counselfornece o comando counsel-agque pesquisa o diretório atual pela string inserida.

Se você deseja pesquisar em um projeto (não apenas no diretório de trabalho atual), adicione-o ao seu .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)))

Esta função será usada agpara pesquisar recursivamente o diretório raiz do projeto.

Editar : a função acima é padronizada com o símbolo no ponto de pesquisa. Se você não gostar desse comportamento, substitua (thing-at-point 'symbol)por nil. E se você quiser que os resultados da pesquisa sejam impressos em bufferC-c C-o

Edit2 : se você ripgrepinstalou, você pode substituí counsel-ag-lo counsel-rgpela função acima e ela também ficará boa :)

Chakravarthy Raghunandan
fonte
É projectile-project-rootpossível encontrar a raiz do projeto Rails?
Boris Stitnicky
Sim, desde que seja um projeto de projétil válido, ele funcionará. Havia algum pacote chamado projectile-rail, eu acho.
Chakravarthy Raghunandan
Por que você nomeou a função rag/...?
Boris Stitnicky
É um estilo comum que as pessoas costumam usar ao escrever suas próprias funções (você pode ver isso sendo feito por muitas outras pessoas se você navegar na configuração do emacs). Todas as funções I definido começam com rag/prefixo e que os torna fácil de encontrar sob M-x:)
Chakravarthy Raghunandan
Ic, que é o seu nome :-)
Boris Stitnicky
2

Aqui está um exemplo de uma função grep personalizada que recebe recursivamente o conteúdo do arquivo (usando uma regexp para o termo de pesquisa) em um diretório e seus subdiretórios e exibe os resultados com linhas de contexto antes e depois. As bibliotecas internas compile.ele grep.elfornecem vários métodos para ir para o local no arquivo que contém os resultados da pesquisa. Não é necessário instalar mais nada para este exemplo funcionar - tudo o que é necessário é uma versão completa do Emacs e um computador com o greputilitário instalado. A variável grep-programpode ser ajustada para apontar para um local específico do greputilitário, se o padrão não for suficiente.

(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 de leis
fonte
Enquanto a pergunta não procura como modificar simultaneamente vários arquivos retornados como resultados pela função grep acima, acho muito útil usar multiple-cursorse wgrep. Faz a modificação de vários arquivos de uma só vez. Essas bibliotecas, no entanto, precisariam ser instaladas se esses recursos forem desejados.
lawlist
Qual é a vantagem de usar isso sobre o builtin rgrep?
N
1
@npostavs - achei que o built-in rgrepconsome tempo para personalizar meus critérios de pesquisa de regex preferidos e escolhi codificar tudo em uma função personalizada (que uso várias vezes ao dia). Se você gostaria de escrever uma resposta alternativa que descreva como personalizar rgrep, isso seria útil para outros leitores do fórum no futuro.
lawlist