Journal Chercher des répertoires bookmark avec un fuzzy finder

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes : aucune
7
4
fév.
2017

Bonjour Nal,

connais-tu fzf ? C'est un outil en ligne de commande qui permet de sélectionner rapidement une ligne dans un fichier : on tape juste quelques caractères et une heuristique associe un score à chaque ligne, nous présentant en premier la ligne faisant le meilleur score. Il y a d'autres outils semblables (fzy, pick, etc.), et il y a souvent des versions intégrées dans les éditeurs de textes comme vim ou emacs pour chercher des fichiers, buffers, tags, ou bien dans les navigateurs pour chercher dans l'historique ou les bookmarks.

Du coup, comme j'aime bien les bookmarks, j'ai fait une petite fonction shell appelée d qui permet de se déplacer dans un dossier bookmarqué en utilisant fzf.

En pratique pour bookmarquer le dossier courant, ça donne :

$ d b

Et pour se déplacer dans un dossier :

$ d

La fonction n'est pas très longue :

# USAGE: d [b]
#
# The function jumps to bookmarked directory selected by user with fzf.  If
# argument "b" is provided, current directory is bookmarked.
#
# The D_BOOKMARKS variable can be set to control bookmarks file location. The
# D_FUZZY_FINDER variable can be set to use a custom fuzzy finder (default is
# "fzf").
#
# The bookmarks file is kept sorted by usage, to allow easy removal by hand of
# unused bookmarks.
#
# Install: just source this file in your shell rc file.
d() {
    local cmd="$1"
    local bookmarks_file="${D_BOOKMARKS:-"$HOME/.d_bookmarks"}"
    local lock_file="${bookmarks_file}.lock"
    local file
    if [ "$cmd" == "b" ]; then
        file="$(pwd)"
    elif [ -n "$cmd" ]; then
        echo "d: unknown argument: '$cmd'. Expected 'b' or no argument." >&2
        return 1
    else
        file="$(<"$bookmarks_file" ${D_FUZZY_FINDER:-fzf})"
    fi
    if [ ! -d "$file" ]; then
        return
    fi
    cd "$file" || {
        echo "d: could not enter '$file' directory" >&2
        return 1
    }
    if mkdir "$lock_file" > /dev/null 2>&1; then
        # create bookmarks file if it does not exist
        touch "$bookmarks_file"
        # put $file at the end of the bookmarks file,
        # to keep it sorted by last time usage.
        perl -i.back -ne 'BEGIN{$f = quotemeta(shift);} print unless /^$f$/;' \
            "$file" "$bookmarks_file" &&
        print -r -- "$file" >> "$bookmarks_file" ||
            echo "d: something went wrong updating bookmarks file" >&2
        rmdir "$lock_file" || {
            echo "d: could not remove lock file '$lock_file'" >&2
            return 1
        }
    else
        echo "d: lock file $lock_file already present" >&2
        return 1
    fi
}

Et puis, comme j'aime bien aussi l'idée de xdg-open pour ouvrir des fichiers en tapant toujours la même commande, j'ai fait un petit script (il s'appelle o celui-ci), qui va utiliser fzf pour sélectionner un fichier à ouvrir. Le script essaie de pas être trop bête et, si le répertoire courant est un dépôt git, il utilisera par exemple git ls-files pour récupérer la liste. S'il semble y avoir trop de fichiers, il limite la profondeur de recherche.

En pratique, c'est normalement :

$ o

Ou, si on veut préciser un répertoire dans lequel effectuer la recherche autre que le répertoire courant :

$ o dossier

Au final, en combinant les deux, j'évite une bonne partie des cd et itérations à coups de <tab> pour me déplacer et ouvrir des fichiers.

Le script o est un peu plus long, du coup j'ai mis les deux scripts ici. En cherchant un peu, j'ai trouvé un ou deux trucs un peu similaires comme ceci pour bash et zsh. J'ai testé mes scripts qu'avec ksh sous OpenBSD, mais a priori ils devraient fonctionner aussi avec bash ou zsh.

Et vous, avez-vous aussi des astuces similaires à partager ?

  • # un probleme ?

    Posté par  . Évalué à 2. Dernière modification le 04 février 2017 à 19:41.

    d B
    pour mettre le dossier B dans les bookmarks

    si je changes de dossier, pour faire genre

    cd /tmp
    operation

    et comment je retourne dans le dossier B ?
    si c'est juste en faisant d ca n'a pas d'interet,
    le shell bash chez moi gere parfaitement le cd - revenir au dernier dossier

    cd tout seul me fait retourner au dossier /home de l'utilisateur courant

    • [^] # Re: un probleme ?

      Posté par  (site web personnel) . Évalué à 2.

      En fait, d b met le dossier courant dans les bookmarks. d dossier ne fait rien, b est l'unique argument possible de d. Ensuite un simple d permet de choisir un bookmark quelconque dans la liste des bookmarks en deux ou trois touches en moyenne. Pour revenir au dossier précédent ou aller au /home j'utilise encore cd moi aussi.

  • # autojump ?

    Posté par  (site web personnel) . Évalué à 3.

    Ça me fait beaucoup penser à autojump, tu as essayé ?

    • [^] # Re: autojump ?

      Posté par  (site web personnel) . Évalué à 2.

      Je n'ai pas essayé (je crois qu'il faut bash ou zsh), mais je connais un peu, oui, le concept m'avait semblé intéressant. En fait, autojump fait plus de choses et pas exactement les mêmes, au sens il fait des stats sur l'historique pour savoir quels sont les répertoires les plus utilisés, plutôt que juste avoir des bookmarks, mais c'est différent aussi pour la recherche, qui n'est pas « fuzzy », il me semble, mais j'ai pas suivi les évolutions.

      Ce serait possible de faire une recherche fuzzy aussi avec l'historique, mais par contre faire des stats n'aurait plus beaucoup de sens. En pratique, j'échange beaucoup vraiment entre un nombre pas énorme de répertoires (quelques dizaines au plus), donc des bookmarks accessibles en quelques touches ça me suffit pour l'instant.

      • [^] # Re: autojump ?

        Posté par  (site web personnel) . Évalué à 2.

        Pas très réveillé hier soir, mais quand je dis pas de recherche « fuzzy », c'est plutôt, pas de recherche intéractive à chaque caractère comme avec fzf, mais je me trompe peut-être. Le matching doit être fuzzy aussi.

  • # Fasd

    Posté par  . Évalué à 5.

    Ce que tu décris me fait penser à fasd: https://github.com/clvv/fasd

    J'installe fasd
    Je visite des répertoires
    là, je tape d foo et j'ai une fuzzy liste de répertoires contenant "foo" dans leur chemin complet. C'est pas juste des bookmarks, c'est un peu plus général. Je l'utilise tout le temps !

    • [^] # Re: Fasd

      Posté par  (site web personnel) . Évalué à 2.

      Je viens de tester, ça a l'air pas mal, en effet. Ceci dit, je crois que je vais rester avec mon d et mon o pour plusieurs raisons.

      La principale, c'est que la recherche « intéractive » de fasd n'est pas vraiment intéractive (peut-être l'est-elle avec bash ou zsh, je sais pas, même si j'en doute). Au sens, avec fzf, même avec des dizaines de milliers d'entrées, au fur et à mesure qu'on écrit on a en temps réel instantanément et sans bloquer l'interface (concurrent) une sélection qui se fait en fonction du chemin, pas besoin de taper un un numéro en regardant une liste, ni d'essayer de prévoir à l'aveugle si on a bien intuité le motif qui réduit la recherche (dans fasd comme dans autojump, d'ailleurs). Et même avec des milliers d'entrées, quelques touches suffisent à sélectionner un item en pratique. Et o a l'avantage qu'il ne nécessite pas d'avoir visité quelque chose avant : si c'est un dépôt git, il fera git ls-files, si c'est un dépôt fossil, fossil ls, et sinon il lance ag -g "" (ou find avec de bonnes options si le silver searcher ag n'est pas là) ce qui permet d'éviter quand même pas mal de fichiers qui n'ont pas l'air d'être du texte.

      Du coup, en pratique, on fait d jusqu'à un répertoire, puis o pour sélectionner un fichier : en fait, o est parfois même utilisable directement depuis le $HOME avec un disque dur non ssd grâce à la limitation de profondeur de recherche lorsqu'il sent qu'il y en a trop. Avec un disque dur ssd, limite tout ça est presque inutile.

      En fait, fzf est tellement fort, qu'il n'a même pas vraiment strictement besoin (même si ça aide) de quelque chose comme o ou d : si locate a sa base de données actualisée, parfois on peut se contenter de fzf uniquement : locate / | fzf ; avec mon ordi pas terrible c'est faisable, même si avec un petit délai : avec plus d'un million de fichiers, en écrivant juste quelques lettres on trouve en fait ce qu'on veut assez vite.

      La deuxième raison, moins importante, c'est que d est un peu plus simple et ne nécessite pas de remplacer tout usage du cd built-in par le fasd_cd.

      • [^] # Re: Fasd

        Posté par  (site web personnel) . Évalué à 2. Dernière modification le 05 février 2017 à 10:29.

        ne nécessite pas de remplacer tout usage du cd built-in par le fasd_cd.

        En fait, j'arrive pas trop à comprendre comment il obtient l'historique de cd, mais bon, ça marche :)

        Edit : en fait, il utilise fc -nl ou history pour reconstruire, on dirait. C'est pas immédiat comme truc.

  • # Petite correction de portabilité

    Posté par  (site web personnel) . Évalué à 3.

    Je me suis aperçu que j'utilisais un ksh-isme (il n'y a pas que les bashismes qui existent !). C'est maintenant corrigé dans le dépôt, et les scripts fonctionnent avec bash.

    Autre chose : en relisant la description de l'outil fzf que je fais, je me rends compte que je précise pas clairement que fzf est un outil écrit en go interactif (ncurses), qui s'actualise avec la frappe (comme la barre de firefox, ou dmenu, etc.).

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.