Wie transponiere ich zwei Argumente einer Funktion in Python?


11

Wie kann ich zwei Argumente in einem Aufruf einer Python-Funktion austauschen?

Wenn ich pointden Abstand zwischen diesen beiden Argumenten einstelle:

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

und dann M-t( transpose-words) bekomme ich:

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

Andererseits bekomme transpose-sexpsich mit CMt ( ):

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

Was ich will ist:

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

Gibt es einen Befehl, der das macht?


2
Ich habe es nicht versucht, aber Anchored Transpose kann dafür verwendet werden. Es ist jedoch ein zweistufiger Prozess.
Kaushal Modi

Es gibt eine Kernfunktion namens, transpose-subrdie eine forwardFunktion aufnimmt und in eine transposeFunktion übersetzt. Wenn wir also hätten c-forward-arglist(Funktion, um von einer Funktion arg zur nächsten zu wechseln - AFAICT existiert nicht), hätten wir c-transpose-arglist.
Brendan

Antworten:


4

Auch das wollte ich schon lange haben, und hier habe ich eine gewisse Motivation gefunden, daran zu arbeiten. Es ist wahrscheinlich nicht sehr robust, aber beim ersten Versuch scheint es die Fälle abzudecken, die ich versucht habe:

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

Ich bekomme dies, wenn der Punkt auf dem Leerzeichen steht (funktioniert, wenn das Komma steht): let *: Falsches Typargument: Ganzzahl-oder-Marker-p, Null
Croad Langshan

@ RoadLangshan das Update war relativ einfach (siehe oben). Die lustige Sache, die ich versuchte, Stefans Rat zu folgen, indem ich eine Alternative schrieb forward-sexp-function, und die schien wegen der Kommas zu umständlich zu sein. Ich habe dann versucht, Nachahmer traspose-sexpzu machen, um dasselbe zu tun, und wieder macht es das wirklich schwierig, Kommas zu berücksichtigen. Ich behaupte nicht, dass dies immer das Richtige ist, wenn es darum geht, Trennzeichen
aufzulisten

Dies schlägt fehl, (aa, bb)wenn sich der Cursor auf dem ersten befindet a. Dies funktioniert auch nicht beim Transponieren. FOO(aaa *, bbb, ccc)Das Transponieren ist *irgendwie durcheinander.
ideasman42

Scheitert auch für FOO(&aaa, bbb)(& tauscht nicht).
ideasman42

4

Ich verwende eine Variation davon transpose-sexps, die nach dem Fall sucht, den Sie beschreiben, und transponiere Dinge, die durch Kommas getrennt sind, oder nur reguläre Dinge transpose-sexps. Außerdem bleibt der Cursor an Ort und Stelle, anstatt ihn nach vorne zu ziehen, was etwas anders ist, aber ich persönlich mag.

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

Für mich bleibt zu Beginn des assertEqual-Aufrufs ein Leerzeichen (beginnend mit dem Punkt auf dem Leerzeichen).
Croad Langshan

1
Dies funktioniert nicht für Schlüsselwortargumente wie: f(a=1, b=2)(es transponiert herum)
Att Righ

1
Während die Frage abount C-Code ist, funktioniert diese Antwort für f(a=1, b=2)- emacs.stackexchange.com/a/47930/2418
ideasman42

2

In Modi, die SMIE verwenden, transpose-sexpsollte dies für diesen Fall korrekt funktionieren. Sie schlagen immer noch fehl, wenn das Infix-Symbol (auch als "Trennzeichen" bezeichnet) kein ,(oder ein ;), sondern ein Wort (z and. B. ) ist.

Ich bin der Meinung, dass der Befehl dafür lautet transpose-sexpund wenn dies nicht richtig funktioniert, betrachte ich ihn als Fehler (aber einen Fehler, der schwierig sein kann und / oder Zeit braucht, um ihn zu beheben und eine niedrige Priorität zu haben, also nicht ' nicht den Atem anhalten). Die Möglichkeit, dies zu beheben, besteht darin forward-sexp-function, eine Funktion festzulegen, die weiß, dass "nachdem ich ein Komma gesehen habe, springe ich einfach um das gesamte Argument herum".


1
Ich denke, der Java-Modus (zum Beispiel) verwendet kein SMIE? Wie würde man das beheben?
Samuel Edwin Ward

1
Nein, in der Tat verwenden die Hauptmodi für C-ähnliche Sprachen SMIE nicht nur aus historischen Gründen: Der Parser von SMIE ist für diese Sprachen einfach zu schwach. Der Parser von SMIE würde jedoch einwandfrei funktionieren, wenn Sie zwei durch Komma getrennte Argumente austauschen möchten (dieser Teil der Grammatik ist einfach genug). Ich denke, es wäre möglich, SMIE zu konfigurieren und zu verwenden, forward-sexp-functionaber Sie hätten es Hinzufügen von Code, um SMIEs nur forward-sexp-function in den Fällen zu verwenden, in denen dies gut genug funktioniert, da dies in vielen anderen Fällen zu verwirrendem Verhalten führen würde.
Stefan
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.