Recherchez dans votre code avec Ack

Si comme moi vous passez la majeure partie de votre temps à éditer du texte ou des documents, vous générez beaucoup de contenu, et vous avez régulièrement besoin de rechercher dans ce contenu. Et il s'avère que rechercher dans un projet de développement présente des spécificités pour lesquelles des outils génériques comme Spotlight montrent vite leurs limites. Dans un environnement unix, vous possédez déjà de plusieurs outils très puissants, parmi lesquels :

  • grep : très connu et polyvalent, supporte pas mal d'options (notamment des regexp). Mais personnellement, je n'ai jamais réussi à mémoriser la syntaxe, les options, l'ordre des paramètres, et la façon de s'en servir.
  • find : puissant mais complexe, bien pour trouver et automatiser des actions sur les fichiers recherchés, mais pas simple à utiliser (il faut combiner un bon nombre d'options).

Besoins

  • Recherche rapide : j'ai de nombreux projets de taille modeste, mais également certains qui comptent plusieurs dizaines de milliers de fichiers. Il fallait donc que l'outil que j'allais trouver soit très rapide. Idéalement, aussi rapide que les recherches lancées à partir d'éditeurs comme Sublime Text ou Atom.
  • Ignorer les fichiers "inutiles" : fichiers liés à mon gestionnaire de versioning, fichiers binaires, etc.
  • Facile à comprendre et mémoriser : find et grep sont puissants mais je suis incapable de mémoriser leurs syntaxes, donc je recherchais un outil plus simple à manipuler au quotidien
  • Utilisation des regexps : je ne vous ai pas dit ? J'adore les regexps :)
  • Compatible avec vim : vim, what else ?

Ack : "beyond grep"

Sur le site officiel de ack, le ton est donné très vite : "ack is a tool like grep, optimized for programmers". Ça s'annonce plutôt bien ! On trouve également en première page les 5 points forts de cet outil :

  • Blazing fast : It's fast because it only searches the stuff it makes sense to search.
  • Better search : Searches entire trees by default while ignoring Subversion, Git and other VCS directories and other files that aren't your source code.
  • Designed for code search : Where grep is a general text search tool, ack is especially for the programmer searching source code. Common tasks take fewer keystrokes.
  • Highly portable : ack is pure Perl, so it easily runs on a Windows installation Perl (like Strawberry Perl) without modifications.
  • Free and open : Ack costs nothing. It's 100% free and open source under Artistic License v2.0.

Autant dire que ça semble répondre parfaitement à mes critères. Maintenant, un petit coup de brew, apt-get, pacman et consorts pour installer ack, et on va voir un peu comment on se sert de cette petite merveille.

Utilisation

Une commande de recherche ack se structure ainsi :

1
$ ack [OPTIONS] MOTIF [FICHIERS] [TYPES-DE-FICHIERS]
  • MOTIF : le motif que vous recherchez. Il peut s'agir d'une chaîne toute simple (ack find_by) ou d'une regexp (ack input.*autofocus)
  • OPTIONS : elles sont assez peu nombreuses et bien pensées, ce qui les rend très faciles à mémoriser (si vous utilisez grep, plusieurs options sont communes aux 2 outils, vous ne serez pas dépaysé). Je vous laisse vous référer à la documentation en ligne pour une liste complète. Pour ma part, j'utilise souvent -i / --ignore-case pour ne pas prendre la casse en compte, mais il y a quelques autres options notables comme -v pour renvoyer les lignes qui ne matchent pas, ou -w pour rechercher uniquement des correspondances sur des mots entiers. Pour ce dernier cas, j'aurais plutôt tendance à utiliser le délimiteur \b des regexp, question d'habitude.
    • En parlant de casse, ack a une gestion de la casse intelligente, avec l'option… --smart-case. Si le motif recherché est entièrement en minuscule, alors il ne prend pas la casse en compte : ack --smart-case post recherchera post / POST / pOsT, etc. En revanche, si le motif recherché comporte au moin une majuscule, alors la recherche sera "case-sensitive". C'est très pratique à l'usage !
  • FICHIERS : fichiers ou répertoires auxquels on limite la recherche, je ne vous fais pas un dessin.
  • TYPES-DE-FICHIERS : il est possible de filtrer les types de fichiers sur lesquels va porter une recherche. Ainsi, --ruby recherche dans tous les fichiers Ruby, et on peut également exclure certains types de fichiers avec --noruby, qui va rechercher dans tous les fichiers sauf les fichiers ruby. Et c'est tout simplement ultra-pratique ! Bon, en lisant ça vous vous dites sûrement : "mouais… pourquoi pas simplement un pattern de recherche en regexp ?". Justement : là où ça s'avère intéressant, c'est que ces types des fichiers sont intelligement pensés : un type de fichier correspond ainsi à un ensemble de patterns. Ainsi, pour reprendre le cas de ruby, le type de fichier --ruby matche tout ça : .rb .rhtml .rjs .rxml .erb .rake .spec; Rakefile; first line matches /^#!.*\bruby/. Wow ! Si on voulait faire ça à la main à chaque recherche, ce serait un peu lourd n'est-ce pas ? Bref, vous l'avez compris, j'adore vraiment cette feature de ack. Et évidemment on peut combiner plusieurs types de fichiers différents. Et ça n'est pas tout…

Création de types de fichiers custom

Ack fournit de nombreux types de fichiers préconfigurés (80 chez moi à l'heure où j'écris ces lignes, avec ack 2.14) : vous pouvez voir le détail des différents types de fichiers avec la commande ack --help-types. Mais vous avez peut-être des besoins particuliers, des types de fichiers custom propres à un projet ou à votre environnement. Et c'est là que ça devient encore plus intéressant : ack vous laisse la main là-dessus et vous permet, si vous le souhaitez, de créer de nouveaux types de fichiers, ou de modifier des types existants.

Par exemple, j'ai défini les types de fichiers suivants dans mon fichier ~/.ackrc avec l'option --type-set :

1
2
3
4
5
6
# ~/.ackrc
--type-set=haml=.haml
--type-set=slim=.slim
--type-set=sass=.sass
--type-set=scss=.scss
--type-set=coffee=.coffee

Peut-être aussi que vous voudrez customiser les types de fichiers existants : pour cela, vous pouvez utiliser l'option --type-add=filetype=pattern qui permet d'ajouter le motif pattern au type de fichier filetype. Voici un autre exemple tiré de mon fichier de config :

1
2
# ~/.ackrc
--type-add=js=.js.erb,.coffee,.coffee.erb

Lorsque je fais une recherche avec le filetype --js, cette ligne me permet donc de rechercher dans mes fichiers .js, mais aussi dans mes fichiers .coffee ainsi que dans les fichiers de scripts .erb dont on a parfois besoin en Rails.

Comme ça, plus de risque d'oublier des fichiers avec une regexp custom, vous définissez vos types de fichiers une fois pour toutes et vous êtes sûr que vos recherches gèrent bien tous les cas de figure !

Thumbs up !

Ack.vim

Je pense que vous commencez à comprendre pourquoi je trouve cet outil si pratique. Vous savez peut-être que je suis également un grand fan de vim (plus exactement neovim depuis de nombreux mois). Et justement, parmi les features qui font défaut à vim, il y a la recherche dans un projet entier. Encore une fois, ack vient à notre rescousse ! Il fut un temps où la meilleure solution était de créer des fonctions personnalisées pour lancer des recherches ack dans le shell, mais aujourd'hui, le plus simple est d'utiliser le plugin quivabieng © : ack.vim. Je vous laisse vous référer à la documentation pour l'installation, rien de bien compliqué.

Utilisation

Côté utilisation, c'est bien simple : si vous savez utiliser ack en ligne de commande, alors vous savez utiliser Ack.vim. Tout fonctionne exactement de la même façon, mêmes options, mêmes comportements. Seule différence que j'ai repérée, je suis obligé d'utiliser des quotes autour de mon motif s'il s'agit d'une regexp. Mis à part ça, tout est identique, et vous pouvez totalement utiliser des commandes comme celles-ci :

1
2
3
:Ack! find_by --ruby spec/
:Ack! select -i -w --nolog
:Ack! "input.*type=('|\")(checkbox|radio)" --html

Les résultats sont ensuite affichés dans une fenêtre de type "quickfix", et vous pouvez facilement les parcourir avec les commandes habituelles de déplacement, et ouvrir chaque fichier avec la touche entrée.

Configuration

Voici la configuration que j'utilise. Le plugin ack.vim a 2 commandes :

  • :Ack : la liste des résultats s'affiche dans une fenêtre quifix, et le 1er résultat s'ouvre directement dans un nouveau buffer.
  • :Ack! : la liste des résultats s'affiche dans une fenêtre quickfix, mais aucun résultat ne s'ouvre tant que ne le demandez pas (et c'est selon moi beaucoup plus logique comme façon de fonctionner).

J'ai donc défini les 2 abbréviations suivantes en mode ligne de commande, pour que toutes mes saisies de la commande Ack soit remplacées par Ack!. Et comme c'était pénible de taper le A en majuscule, j'ai également mis une abbréviation pour que ack soit transformé en Ack!. Et hop :)

1
2
3
4
" Map Ack command to Ack! automatically so that
" it doesn't open the first item by default
ca Ack Ack!
ca ack Ack!

Bref, ack est un outil dont je me sers depuis plusieurs années et des dizaines de fois chaque jour, et je pourrais difficilement envisager de m'en passer aujourd'hui. J'espère que cet article vous aura donné envie d'essayer, et peut-être améliorera un peu votre quotidien, comme ç'a été le cas pour moi !

Références

  • beyondgrep.com : le site officiel du projet ack
  • mon ackrc
  • ack.vim
  • Ag / the silver searcher : un fork de ack qui a fait son chemin depuis. Encore plus performant mais moins bien documenté, et je ne suis pas sûr qu'il permette de faire tout ce que fait Ack (notamment personnaliser les types de fichier). Un outil intéressant en tout cas.