Combinez deux fonctions interactives

Sep 19 2020

Par exemple, je souhaite trier un fichier, puis supprimer les lignes en double. Je cours M-x sortsuivi de M-x delete-duplicate-lines. Je voulais les combiner en une seule fonction interactive. Voici une tentative pour résoudre ce problème.

(defun sort-and-dedup ()
 ""
 (interactive)
 (sort)
 (delete-duplicate-lines)
)

Cela donne une erreur indiquant que les fonctions sous-jacentes n'ont pas un nombre suffisant d'arguments. Je comprends en quelque sorte pourquoi c'est le cas (l'argument interactif vs non interactif, parmi quelques autres), mais je ne sais pas quoi faire exactement. Pouvez-vous me dire comment corriger cela?

Modifier 1:

Le code ci-dessus est erroné à bien des égards. Regardez la réponse acceptée pour plus d'informations. Et merci pour l'aide.

Modifier 2:

C'est ce que j'avais l'intention de faire.

(defun sort-and-dedup-region (beg end)
  (interactive "r")
  (sort-lines nil beg end)
  (delete-duplicate-lines beg end nil t))

(defun sort-and-dedup ()
  (interactive)
  (sort-and-dedup-region
   (point-min)
   (point-max)))

Réponses

4 NickD Sep 19 2020 at 01:48

[Je crois que vous vouliez dire sort-lines, non sort- c'est la seule façon dont la question a du sens, donc je vais l'assumer.]

Les deux sort-lineset delete-duplicate-linesfonctionnent sur la région sélectionnée . Vous devez sélectionner une région avant de les appeler, sinon ils se plaindront qu'il n'y a pas de région. Vous allez donc devoir faire la même chose dans votre fonction: supposer qu'on vous donne une région et vous plaindre s'il n'y en a pas.

Une région est spécifiée par deux positions dans le tampon, classiquement nommées BEGet `END. Donc, votre fonction ressemblera à ceci:

(defun sort-and-dedup (beg end)
   (interactive <mumble>)
   (sort-lines nil beg end)
   (delete-duplicate-lines beg end))

Le premier argument pour trier les lignes lui indique s'il faut trier dans l'ordre inverse.

Lorsque vous appelez la fonction de manière interactive, vous spécifiez une région en définissant une marque à une extrémité, puis en déplaçant le point à l'autre extrémité (ou en commençant à une extrémité et en faisant glisser la souris jusqu'à l'autre extrémité). Alors, comment communiquez-vous ces positions à la fonction? En donnant un argument à interactive: si vous regardez sa doc string (avec C-h f interactive RET), vous trouverez que rc'est ce dont vous avez besoin pour spécifier une région - Emacs s'arrangera pour traduire la région que vous avez choisie (comme vous l'avez choisie) en une paire des positions (BEG et END) qui seront passées à votre fonction.

Donc, la fonction ressemble à ceci:

(defun sort-and-dedup (beg end)
   (interactive "r")
   (sort-lines nil beg end)
   (delete-duplicate-lines beg end))

Alternativement, comme le suggère @Dan, vous pouvez utiliser call-interactivelyet laisser chaque fonction déterminer ce dont elle a besoin. Mais vous devrez tout de même spécifier une région au préalable:

(defun sort-and-dedup ()
   (interactive)
   (call-interactively #'sort-lines)
   (call-interactively #'delete-duplicate-lines))

EDIT: en incorporant la suggestion de @ phil (et maintenant je vois que vous avez réellement implémenté ceci et l'avez ajouté à votre question), vous pouvez écrire une fonction qui appelle (la première définition de) sort-and-dedupavec les arguments nécessaires pour opérer sur tout le tampon:

(defun sort-and-dedup-whole-buffer()
   (interactive)
   (sort-and-dedup (point-min) (point-max)))

Vous pouvez le lier à une séquence de touches si vous comptez le faire fréquemment:

(define-key global-map (kbd "M-S-<f10>" #'sort-and-dedup-whole-buffer)

bien que personnellement je ne le ferais pas: les séquences de touches sont une denrée rare, donc je l'essayerais M-x sort-and-dedup-whole-buffer RETpendant un certain temps; si cela devient une nuisance, alors je le lierai à une séquence de touches.

Je revisite également toutes mes définitions clés tous les deux ans et récupère celles que je n'utilise plus. Je les garde dans leur propre fichier, chargé explicitement par mon fichier init, afin que je puisse les trouver et les revoir facilement.