Comment transposer deux arguments d'une fonction en Python?

11

Comment puis-je échanger deux arguments dans un appel à une fonction Python?

Si je mets pointl'espace entre ces deux arguments:

self.assertEqual(json.loads(some.data), json_data)

puis M-t( transpose-words), j'obtiens:

self.assertEqual(json.loads(some.json), data_data)

Par contre avec CMt ( transpose-sexps) j'obtiens:

self.assertEqual(json.loadsjson_data, (some.data))

Ce que je veux c'est:

self.assertEqual(json_data, json.loads(some.data))

Y a-t-il une commande qui fera ça?

Croad Langshan
la source
2
Je n'ai pas essayé mais Anchored Transpose peut être utilisé pour cela; c'est, cependant, un processus en 2 étapes.
Kaushal Modi
Il existe une fonction de base appelée transpose-subrqui prend une forwardfonction et la traduit en transposefonction. Donc, si nous avions c-forward-arglist(fonction pour passer d'une fonction arg à la suivante - AFAICT cela n'existe pas), nous l'aurions c-transpose-arglist.
Brendan

Réponses:

4

C'est quelque chose que je voulais aussi avoir depuis longtemps, et j'ai trouvé ici une motivation pour y travailler. Ce n'est probablement pas très robuste, mais au premier essai, il semble couvrir les cas que j'ai essayés:

(defun my/calculate-stops ()
  (save-excursion
    (let ((start
           (condition-case e
               (while t (backward-sexp))
             (error (point))))
          stops)
      (push start stops)
      (condition-case e
          (while t
            (forward-sexp)
            (when (looking-at "\\s-*,")
              (push (point) stops)))
        (error (push (point) stops)))
      (nreverse stops))))

(defun my/transpose-args ()
  (interactive)
  (when (looking-at "\\s-") (backward-sexp))
  (cl-loop with p = (point)
           with previous = nil
           for stop on (my/calculate-stops)
           for i upfrom 0
           when (<= p (car stop)) do
           (when previous
             (let* ((end (cadr stop))
                    (whole (buffer-substring previous end))
                    middle last)
               (delete-region previous end)
               (goto-char previous)
               (setf middle (if (> i 1) (- (car stop) previous)
                              (string-match "[^, \\t]" whole 
                                            (- (car stop) previous)))
                     last (if (> i 1) (substring whole 0 middle)
                            (concat (substring whole (- (car stop) previous) middle)
                                    (substring whole 0 (- (car stop) previous)))))
               (insert (substring whole middle) last)))
           (cl-return)
           end do (setf previous (car stop))))
wvxvw
la source
J'obtiens ceci lorsque le point est sur l'espace (fonctionne lorsque sur la virgule): let *: Argument de type incorrect: integer-or-marker-p, nil
Croad Langshan
@CroadLangshan, le correctif a été relativement facile (voir ci-dessus). La chose amusante que j'ai essayé de suivre les conseils de Stefan en écrivant une alternative forward-sexp-function, et qui semblait trop lourde à cause des virgules. J'ai ensuite essayé de copier traspose-sexppour faire la même chose, et, encore une fois, avoir à tenir compte des virgules rend cela vraiment difficile. Je ne prétends pas que cela fait toujours la bonne chose quand il s'agit de lister les délimiteurs, peut-être seulement la moitié du temps ...
wvxvw
Cela échoue (aa, bb)lorsque le curseur est sur le premier a. Cela ne pas non plus le travail de transposition FOO(aaa *, bbb, ccc)Les *dégâts en quelque sorte jusqu'à la transposition.
ideasman42
Échoue également pour FOO(&aaa, bbb)(et ne change pas).
ideasman42
4

J'utilise une variation de ce transpose-sexpsqui regarde le cas que vous décrivez et transpose les choses séparées par des virgules, ou tout simplement fait régulièrement transpose-sexps. Il laisse également le curseur en place au lieu de le faire glisser vers l'avant, ce qui est un peu différent mais j'aime personnellement.

(defun my-transpose-sexps ()
  "If point is after certain chars transpose chunks around that.
Otherwise transpose sexps."
  (interactive "*")
  (if (not (looking-back "[,]\\s-*" (point-at-bol)))
      (progn (transpose-sexps 1) (forward-sexp -1))
    (let ((beg (point)) end rhs lhs)
      (while (and (not (eobp))
                  (not (looking-at "\\s-*\\([,]\\|\\s)\\)")))
        (forward-sexp 1))
      (setq rhs (buffer-substring beg (point)))
      (delete-region beg (point))
      (re-search-backward "[,]\\s-*" nil t)
      (setq beg (point))
      (while (and (not (bobp))
                  (not (looking-back "\\([,]\\|\\s(\\)\\s-*" (point-at-bol))))
        (forward-sexp -1))
      (setq lhs (buffer-substring beg (point)))
      (delete-region beg (point))
      (insert rhs)
      (re-search-forward "[,]\\s-*" nil t)
      (save-excursion
        (insert lhs)))))
scottfrazer
la source
Pour moi, cela laisse un espace au début de l'appel assertEqual (en commençant par un point sur l'espace).
Croad Langshan
1
Cela ne fonctionne pas pour les arguments de mots clés comme: f(a=1, b=2)(il transpose autour)
Att Righ
1
Bien que la question porte sur du code C, cette réponse fonctionne pour f(a=1, b=2)- emacs.stackexchange.com/a/47930/2418
ideasman42
2

Dans les modes qui utilisent SMIE, transpose-sexpdevrait fonctionner correctement pour ce cas. Ils échoueront quand le symbole infixe (aka "séparateur") n'est pas un ,(ou un ;) mais un mot (par exemple and).

Donc, mon avis est que la commande pour cela est transpose-sexpet quand cela ne fonctionne pas correctement, je le considère comme un bug (mais un bug qui peut être difficile et / ou prendre du temps à corriger et à faible priorité, alors ne le faites pas ' t retenez votre souffle). La façon de le résoudre est de définir forward-sexp-functionune fonction qui saura que "après avoir vu une virgule, je saute tout autour de l'argument".

Stefan
la source
1
Je suppose que le mode java (par exemple) n'utilise pas SMIE? Comment y remédier?
Samuel Edwin Ward
1
Non, en effet, les principaux modes pour les langages de type C n'utilisent pas SMIE et pas seulement pour des raisons historiques: l'analyseur de SMIE est tout simplement trop faible pour ces langages. Cela dit, l'analyseur de SMIE fonctionnerait très bien si vous voulez échanger deux arguments séparés par une virgule (cette partie de la grammaire est assez simple), donc je suppose qu'il serait possible de configurer SMIE et de l'utiliser pour forward-sexp-functionmais vous auriez ajouter du code pour n'utiliser SMIE que forward-sexp-function dans les cas où cela fonctionne assez bien, car dans de nombreux autres cas, cela entraînerait un comportement déroutant.
Stefan