¿Cómo transponer dos argumentos de una función en Python?

11

¿Cómo puedo intercambiar dos argumentos en una llamada a una función de Python?

Si pongo pointel espacio entre estos dos argumentos:

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

y luego M-t( transpose-words), obtengo:

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

Por otro lado con CMt ( transpose-sexps) obtengo:

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

Lo que quiero es:

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

¿Hay un comando que haga eso?

Croad Langshan
fuente
2
No lo he intentado, pero Anchored Transpose se puede utilizar para esto; Sin embargo, es un proceso de 2 pasos.
Kaushal Modi
Hay una función central llamada transpose-subrque toma una forwardfunción y la traduce en una transposefunción. Entonces, si tuviéramos c-forward-arglist(función para pasar de una función arg a la siguiente - AFAICT esto no existe) tendríamos c-transpose-arglist.
Brendan

Respuestas:

4

Esto es algo que yo también quería tener durante mucho tiempo, y aquí he encontrado algo de motivación para trabajar en ello. Probablemente no sea muy robusto, pero al primer intento parece cubrir los casos que probé:

(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
fuente
Obtengo esto cuando el punto está en el espacio (funciona cuando está en la coma): let *: Argumento de tipo incorrecto: entero-o-marcador-p, nulo
Croad Langshan
@CroadLangshan la solución fue relativamente fácil (ver arriba). Lo curioso que intenté seguir fue el consejo de Stefan escribiendo una alternativa forward-sexp-function, y eso parecía ser demasiado engorroso debido a las comas. Luego traté de copiar traspose-sexppara hacer lo mismo y, una vez más, tener que tener en cuenta las comas hace que esto sea realmente difícil. No pretendo esto siempre hace lo correcto cuando se trata de los delimitadores de la lista, tal vez sólo la mitad de las veces ...
wvxvw
Esto falla (aa, bb)cuando el cursor está en el primero a. Esto tampoco funciona para la transposición. FOO(aaa *, bbb, ccc)La transposición de *alguna manera arruina.
ideasman42
También falla para FOO(&aaa, bbb)(y no se intercambia).
ideasman42
4

Utilizo una variación de transpose-sexpsese aspecto para el caso que usted describe y transpone cosas separadas por comas, o simplemente lo hace de forma regular transpose-sexps. También deja el cursor en su lugar en lugar de arrastrarlo hacia adelante, lo cual es un poco diferente, pero personalmente me gusta.

(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
fuente
Para mí, esto deja espacios en blanco al comienzo de la llamada afirmar Igual (comenzando con un punto en el espacio).
Croad Langshan
1
Esto no funciona para argumentos de palabras clave como: f(a=1, b=2)(se transpone)
Att Righ
1
Si bien la pregunta es sobre el código C, esta respuesta funciona para f(a=1, b=2)- emacs.stackexchange.com/a/47930/2418
ideasman42
2

En los modos que usan SMIE, transpose-sexpdebería funcionar correctamente para ese caso. Todavía fallarán cuando el símbolo infijo (también conocido como "separador") no sea una ,(o una ;) sino una palabra (por ejemplo and).

Entonces, mi opinión es que el comando para eso es transpose-sexpy cuando esto no funciona correctamente, lo considero un error (pero un error que puede ser difícil y / o tomar tiempo para solucionarlo y tener baja prioridad, así que no lo haga) No aguantes la respiración). La forma de solucionarlo es estableciendo forward-sexp-functionuna función que sepa que "después de ver una coma, simplemente salteé todo el argumento".

Stefan
fuente
1
¿Supongo que el modo Java (por ejemplo) no usa SMIE? ¿Cómo se podría arreglar eso?
Samuel Edwin Ward,
1
No, de hecho, los modos principales para lenguajes tipo C no usan SMIE y no solo por razones históricas: el analizador de SMIE es demasiado débil para esos lenguajes. Dicho esto, el analizador de SMIE funcionaría bien si desea intercambiar dos argumentos separados por una coma (esta parte de la gramática es lo suficientemente simple), por lo que supongo que sería posible configurar SMIE y usarlo, forward-sexp-functionpero tendría que agregar algún código para usar solo SMIE forward-sexp-function en los casos en que funciona lo suficientemente bien, ya que en muchos otros casos daría lugar a un comportamiento confuso.
Stefan