Ajouter des arguments de la commande précédente à l'achèvement de zsh

22

Dans zsh (ainsi que bash), vous pouvez utiliser des extensions de mots d'historique pour désigner les arguments des commandes précédentes.

Cet exemple montre comment obtenir le 2e paramètre de la commande précédente dans l'historique avec !:#expansion:

% echo foo bar baz
foo bar baz
% echo !:2
echo bar
bar

J'oublie souvent exactement le paramètre # d'un argument particulier et la frappe !:#n'est pas toujours aussi rapide quand je me souviens de quel argument il s'agit. Je suis sur le point meta-.de remplacer le dernier argument, mais parfois ce n'est pas le dernier argument que je veux.

Je voudrais ajouter les arguments de la commande précédente comme suggestions pour terminer toute commande que je tape dans zsh.

J'ai pu comprendre comment créer une fonction shell qui peut créer un tableau d'arguments (0..N) à partir de la dernière commande et le lier à une commande particulière.

_last_command_args() {
    last_command=$history[$[HISTCMD-1]]
    last_command_array=("${(s/ /)last_command}") 
    _sep_parts last_command_array
}

# trying to get last_command_args to be suggested for any command, this just works for foo
compdef _last_command_args foo

Voici à quoi cela ressemble pour terminer juste foo où j'ai appuyé sur la touche de tabulation à <TAB>:

% echo bar baz qux
bar baz qux
% foo <TAB>
bar   baz   echo  qux 

Cela fonctionne très bien pour terminer la commande "foo", mais j'aimerais que ce soient des options sur n'importe quelle extension zsh que je fais. Je pense que cela a quelque chose à voir avec le completer zstyle, mais après quelques heures de piratage, j'ai réalisé que j'étais hors de ma profondeur.

Comment puis-je obtenir les arguments de ma commande précédente en tant que compléments suggérés pour toute commande dans zsh?

J'ai mon fichier compinstall zshrc complet partagé sur bitbucket si cela aide. Beaucoup d'entre eux proviennent de diverses sources et j'ai en partie piraté moi-même.

MISE À JOUR:

@La réponse de Julien Nicoulaud m'a rapproché, je la marque comme acceptée car elle m'a conduit là où je devais aller.

Avec ma configuration particulière, en utilisant le suggéré:

zstyle ':completion:*' completer _last_command_args _complete

Cela ne fonctionnait pas tout à fait pour moi car cela provoquait la complétion de l'onglet pour afficher uniquement la liste des arguments de la dernière commande (bien qu'elle se termine également avec les noms de fichiers, mais pas pour les afficher). Changer l'ordre a _complete _last_command_argsfait l'inverse. Il afficherait les noms de fichiers normaux, mais pas last_command_args

Je suppose que cela a quelque chose à voir avec le fonctionnement du completer. Je pense qu'il n'affiche que la sortie de la première méthode qui revient avec succès, mais j'ai du mal à analyser la source zsh pour comprendre pleinement ce qui se passe. J'ai pu modifier ma méthode pour inclure un appel à _completeafin qu'il affiche à la fois les dernières commandes d'argument ainsi que les éléments de saisie semi-automatique standard. Pas tout à fait aussi séparé, mais fonctionne assez bien pour moi.

Voici la fonction complète que j'ai utilisée avec les autres trucs zstyle que j'ai:

# adds the arguments from the last commadn to the autocomplete list
# I wasn't able to get this to work standalone and still print out both regular
# completion plus the last args, but this works well enough.
_complete_plus_last_command_args() {
    last_command=$history[$[HISTCMD-1]]
    last_command_array=("${(s/ /)last_command}") 
    _sep_parts last_command_array
    _complete 
}


_force_rehash() {
  (( CURRENT == 1 )) && rehash
  return 1  # Because we didn't really complete anything
}

zstyle ':completion:::::' completer _force_rehash _complete_plus_last_command_args _approximate 

D'autres lignes zstyle que j'ai, pas nécessaires pour que cela fonctionne, mais pourraient affecter pourquoi cela fonctionne pour moi:

zstyle -e ':completion:*:approximate:*' max-errors 'reply=( $(( ($#PREFIX + $#SUFFIX) / 3 )) )'
zstyle ':completion:*:descriptions' format "- %d -"
zstyle ':completion:*:corrections' format "- %d - (errors %e})"
zstyle ':completion:*:default' list-prompt '%S%M matches%s'
zstyle ':completion:*' group-name ''
zstyle ':completion:*:manuals' separate-sections true
zstyle ':completion:*' menu select
zstyle ':completion:*' verbose yes

Maintenant, si je suis dans un répertoire avec file1.txtet file2.txt, et ma dernière commande était echo foo bar baz, j'obtiens ceci pour la saisie semi-automatique qui est exactement ce que je voulais:

% ls
bar   baz   echo  foo 
- files -
file1.txt   file2.txt 
Ted Naleid
la source
2
C'est une idée spectaculaire
Michael Mrozek
2
Pour compléter ^[ .( insert-last-word), j'aime bien copy-earlier-word, qui passe d'un mot à l'autre de la ligne atteinte par insert-last-word. Pour votre .zshrc:autoload copy-earlier-word && zle -N copy-earlier-word && bindkey '^[,' copy-earlier-word
Gilles 'SO- arrête d'être méchant'
@Gilles merci! Cela me rapproche de la fonctionnalité si je ne parviens pas à obtenir l'auto-complétion complète. ~ 80% du temps, si ce n'est pas le dernier argument, c'est l'avant-dernier argument que je veux et C-. + C-, c'est plutôt bien.
Ted Naleid

Réponses:

6

Vous pouvez ajouter votre complément à la liste des finisseurs utilisés par défaut:

zstyle ':completion:*' completer _last_command_args _complete
nicoulaj
la source
1
S'il y a des _last_command_argsachèvements, les autres ne sont pas affichés. Comment les obtenez-vous en plus des habituels?
Gilles 'SO- arrête d'être méchant'
1
Merci pour la réponse, cela m'a rapproché suffisamment pour que quelque chose fonctionne pour moi, donc je l'accepte. Ce que j'ai fait différemment est noté comme une mise à jour de la question ci-dessus. Votre solution n'a pas imprimé à la fois les arguments de la dernière commande ainsi que les fichiers, etc. à partir du complément normal. J'ai composé _completeà l'intérieur de la fonction que j'ai créée et cela fonctionne exactement comme je le voulais.
Ted Naleid
Vous avez raison, ma réponse n'est pas correcte.
nicoulaj