Come trasporre due argomenti di una funzione in Python?


11

Come posso scambiare due argomenti in una chiamata a una funzione Python?

Se inserissi pointlo spazio tra questi due argomenti:

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

e poi M-t( transpose-words), ottengo:

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

D'altra parte con CMt ( transpose-sexps) ottengo:

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

Quello che voglio è:

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

C'è un comando che lo farà?


2
Non ho provato ma Anchored Transpose può essere usato per questo; è, tuttavia, un processo in 2 passaggi.
Kaushal Modi,

Esiste una funzione principale chiamata transpose-subrche accetta una forwardfunzione e la traduce in una transposefunzione. Quindi se avessimo c-forward-arglist(funzione per passare da una funzione all'altra - AFAICT questo non esiste) avremmo c-transpose-arglist.
Brendan,

Risposte:


4

Questo è qualcosa che anch'io volevo avere da molto tempo, e qui ho trovato un po 'di motivazione per lavorarci su. Probabilmente non è molto robusto, ma al primo tentativo sembra coprire i casi che ho provato:

(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))))

Ottengo questo quando il punto è nello spazio (funziona quando si trova sulla virgola): let *: Argomento di tipo errato: numero intero o marker-p, zero
Croad Langshan

@CroadLangshan la correzione è stata relativamente semplice (vedi sopra). La cosa divertente che ho provato a seguire il consiglio di Stefan scrivendo un'alternativa forward-sexp-function, e che sembrava troppo ingombrante a causa delle virgole. Ho quindi provato a copiare traspose-sexpper fare la stessa cosa e, ancora una volta, dover rendere conto delle virgole rende tutto ciò molto difficile. Non pretendo che questo faccia sempre la cosa giusta quando si parla di delimitatori, forse solo la metà delle volte ...
wvxvw,

Questo non riesce (aa, bb)quando il cursore si trova sul primo a. Anche questo non funziona per la trasposizione. FOO(aaa *, bbb, ccc)In *qualche modo si incasina la trasposizione.
ideasman42

Inoltre fallisce per FOO(&aaa, bbb)(e non scambia).
ideasman42

4

Uso una variante di transpose-sexpsciò che cerca il caso che descrivi e traspone le cose separate da virgole, o semplicemente fa regolarmente transpose-sexps. Inoltre lascia il cursore in posizione invece di trascinarlo in avanti, il che è un po 'diverso ma personalmente mi piace.

(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)))))

Per me questo lascia spazio all'inizio della chiamata assertEqual (a partire dal punto sullo spazio).
Croad Langshan,

1
Questo non funziona per argomenti di parole chiave come: f(a=1, b=2)(traspone in giro)
Att Righ,

1
Mentre la domanda riguarda il codice C, questa risposta funziona per f(a=1, b=2)- emacs.stackexchange.com/a/47930/2418
ideasman42

2

Nelle modalità che usano SMIE, transpose-sexpdovrebbero funzionare correttamente per quel caso. Falliranno comunque quando il simbolo infisso (aka "separatore") non è un ,(o un ;) ma è una parola (ad es and.).

Quindi, la mia opinione è che il comando per quello è transpose-sexpe quando questo non funziona correttamente, lo considero un bug (ma un bug che può essere difficile e / o richiedere del tempo per risolvere e avere bassa priorità, quindi non trattenere il respiro). Il modo per risolverlo è impostare forward-sexp-functionuna funzione che sappia che "dopo che vedo una virgola, faccio un salto sull'intero argomento".


1
Immagino che la modalità java (per esempio) non usi SMIE? Come si potrebbe risolvere il problema?
Samuel Edwin Ward,

1
No, in effetti, le principali modalità per le lingue simil-C non usano SMIE e non solo per ragioni storiche: il parser di SMIE è troppo debole per quelle lingue. Detto questo, il parser di SMIE funzionerebbe bene se vuoi scambiare due argomenti separati da una virgola (questa parte della grammatica è abbastanza semplice), quindi immagino che sarebbe possibile configurare SMIE e usarlo per forward-sexp-functionma avresti dovuto aggiungere del codice per utilizzare SMIE solo forward-sexp-function nei casi in cui funziona abbastanza bene, poiché in molti altri casi si tradurrebbe in un comportamento confuso.
Stefan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.