Jak transponować dwa argumenty funkcji w Pythonie?

11

Jak mogę zamienić dwa argumenty w wywołaniu funkcji Python?

Jeśli wstawię pointspację między tymi dwoma argumentami:

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

a następnie M-t( transpose-words) otrzymuję:

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

Z drugiej strony dzięki CMt ( transpose-sexps) otrzymuję:

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

Chcę to:

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

Czy istnieje polecenie, które to zrobi?

Croad Langshan
źródło
2
Nie próbowałem, ale do tego można użyć zakotwiczonej transpozycji ; jest to jednak proces dwuetapowy.
Kaushal Modi
Istnieje tak zwana funkcja podstawowa, transpose-subrktóra bierze forwardfunkcję i tłumaczy ją na transposefunkcję. Więc gdybyśmy mieli c-forward-arglist(funkcja przechodzenia z jednej funkcji arg do następnej - AFAICT to nie istnieje), mielibyśmy c-transpose-arglist.
Brendan,

Odpowiedzi:

4

To też chciałem mieć od dłuższego czasu, a tutaj znalazłem motywację do pracy nad tym. Prawdopodobnie nie jest zbyt solidny, ale przy pierwszej próbie wydaje się, że obejmuje przypadki, które próbowałem:

(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
źródło
Rozumiem to, gdy punkt jest na spacji (działa, gdy jest na przecinku): let *: Błędny typ argumentu: liczba całkowita lub znacznik-p, zero
Croad Langshan
@CroadLangshan poprawka była stosunkowo łatwa (patrz wyżej). Zabawne, że próbowałem zastosować się do rady Stefana, pisząc alternatywę forward-sexp-function, i to wydawało się zbyt kłopotliwe z powodu przecinków. Następnie próbowałem naśladować, traspose-sexpaby zrobić to samo i znowu, konieczność uwzględnienia przecinków sprawia, że ​​jest to naprawdę trudne. Nie twierdzę, że zawsze robi to dobrze, jeśli chodzi o ograniczniki listy, może tylko przez połowę czasu ...
wvxvw
Nie udaje się to, (aa, bb)gdy kursor znajduje się na pierwszym a. To również nie działa w przypadku transpozycji FOO(aaa *, bbb, ccc). *Jakoś to popsuło transpozycję.
ideasman42
Również nie powiedzie się dla FOO(&aaa, bbb)(i nie zamienia).
ideasman42
4

Używam odmiany transpose-sexpstego wyglądu opisanego przypadku i transponuje rzeczy oddzielone przecinkami lub po prostu regularnie transpose-sexps. Pozostawia również kursor w miejscu zamiast przeciągania go do przodu, co jest nieco inne, ale ja osobiście lubię.

(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
źródło
Dla mnie pozostawia to spację na początku wywołania assertEqual (zaczynając od punktu na spacji).
Croad Langshan
1
Nie działa to dla argumentów słów kluczowych takich jak: f(a=1, b=2)(transponuje wokół)
Att Righ
1
Chociaż pytanie dotyczy kodu C, ta odpowiedź działa dla f(a=1, b=2)- emacs.stackexchange.com/a/47930/2418
ideasman42
2

W trybach, które używają SMIE, transpose-sexppowinny działać poprawnie w tym przypadku. Nadal nie powiedzie się, gdy symbol infix (aka „separator”) nie jest a ,(lub a ;), ale jest słowem (np and.).

Moim zdaniem jest to polecenie, transpose-sexpa gdy to nie działa poprawnie, uważam to za błąd (ale błąd, który może być trudny i / lub wymaga czasu, aby go naprawić i mieć niski priorytet, więc nie wstrzymaj oddech). Aby to naprawić, należy ustawić forward-sexp-functionfunkcję, która będzie wiedziała, że ​​„po zobaczeniu przecinka po prostu przeskakuję cały argument”.

Stefan
źródło
1
Myślę, że tryb java (na przykład) nie używa SMIE? Jak można to naprawić?
Samuel Edwin Ward
1
Nie, w rzeczywistości główne tryby dla języków podobnych do C nie używają SMIE i nie tylko z powodów historycznych: parser SMIE jest po prostu zbyt słaby dla tych języków. To powiedziawszy, parser SMIE będzie działał dobrze, jeśli chcesz zamienić dwa argumenty oddzielone przecinkiem (ta część gramatyki jest dość prosta), więc myślę, że można skonfigurować SMIE i użyć go, forward-sexp-functionale będziesz miał dodać kod, aby używać SMIE tylko forward-sexp-function w tych przypadkach, w których działa wystarczająco dobrze, ponieważ w wielu innych przypadkach spowodowałoby to zamieszanie.
Stefan