Bagaimana cara mengubah dua argumen fungsi dalam Python?

11

Bagaimana saya bisa menukar dua argumen dalam panggilan ke fungsi Python?

Jika saya menempatkan pointspasi di antara dua argumen ini:

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

dan kemudian M-t( transpose-words), saya mendapatkan:

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

Di sisi lain dengan CMt ( transpose-sexps) saya mendapatkan:

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

Yang saya inginkan adalah:

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

Apakah ada perintah yang akan melakukan itu?

Langshan Croad
sumber
2
Saya belum mencoba tetapi Transpored yang Ditabuh dapat digunakan untuk ini; itu, bagaimanapun, proses 2 langkah.
Kaushal Modi
Ada fungsi inti yang disebut transpose-subryang mengambil forwardfungsi dan menerjemahkannya ke dalam transposefungsi. Jadi jika kita punya c-forward-arglist(fungsi untuk berpindah dari satu fungsi arg ke yang berikutnya - AFAICT ini tidak ada) kita akan memilikinya c-transpose-arglist.
Brendan

Jawaban:

4

Ini adalah sesuatu yang saya juga ingin miliki untuk waktu yang lama, dan di sini saya menemukan motivasi untuk mengerjakannya. Ini mungkin tidak terlalu kuat, tetapi pada percobaan pertama tampaknya mencakup kasus-kasus yang saya coba:

(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
sumber
Saya mendapatkan ini ketika titik ada di spasi (berfungsi saat koma): let *: Argumen tipe yang salah: integer-atau-marker-p, nil
Croad Langshan
@CroadLangshan perbaikannya relatif mudah (lihat di atas). Lucunya aku mencoba mengikuti saran Stefan dengan menulis alternatif forward-sexp-function, dan itu kelihatannya terlalu rumit karena koma. Saya kemudian mencoba untuk meniru traspose-sexpuntuk melakukan hal yang sama, dan, sekali lagi, harus memperhitungkan koma membuat ini sangat sulit. Saya tidak mengklaim ini selalu melakukan hal yang benar ketika datang ke daftar pembatas, mungkin hanya separuh waktu ...
wvxvw
Ini gagal (aa, bb)ketika kursor berada di yang pertama a. Ini juga tidak bekerja untuk mentranspose FOO(aaa *, bbb, ccc)The *entah bagaimana messes up mentransposisi.
ideasman42
Juga gagal untuk FOO(&aaa, bbb)(& tidak menukar).
ideasman42
4

Saya menggunakan variasi transpose-sexpsyang terlihat untuk kasus yang Anda gambarkan dan mentransposasikan hal-hal yang dipisahkan oleh koma, atau hanya biasa saja transpose-sexps. Ini juga meninggalkan kursor di tempat bukannya menyeretnya ke depan, yang sedikit berbeda tapi saya pribadi suka.

(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
sumber
Bagi saya ini meninggalkan spasi di awal panggilan assertEqual (dimulai dengan titik pada spasi).
Croad Langshan
1
Ini tidak berfungsi untuk argumen kata kunci seperti: f(a=1, b=2)(transpos sekitar)
Att Righ
1
Sementara pertanyaannya adalah jumlah kode C, jawaban ini berfungsi untuk f(a=1, b=2)- emacs.stackexchange.com/a/47930/2418
ideasman42
2

Dalam mode yang menggunakan SMIE, transpose-sexpharus berfungsi dengan benar untuk kasus itu. Mereka masih akan gagal ketika simbol infiks (alias "pemisah") bukan a ,(atau a ;) tetapi kata (misalnya and).

Jadi, pendapat saya adalah bahwa perintah untuk itu adalah transpose-sexpdan ketika ini tidak berfungsi dengan benar, saya menganggapnya sebagai bug (tetapi bug yang mungkin sulit dan / atau membutuhkan waktu untuk diperbaiki dan memiliki prioritas rendah, jadi jangan ' t tahan nafasmu). Cara untuk memperbaikinya adalah dengan menetapkan forward-sexp-functionke fungsi yang akan tahu bahwa "setelah saya melihat koma, saya hanya melompati seluruh argumen".

Stefan
sumber
1
Saya kira java-mode (misalnya) tidak menggunakan SMIE? Bagaimana cara memperbaikinya?
Samuel Edwin Ward
1
Tidak, memang, mode utama untuk bahasa mirip C tidak menggunakan SMIE dan tidak hanya karena alasan historis: Pengurai SMIE terlalu lemah untuk bahasa tersebut. Ini mengatakan, parser SMIE akan berfungsi dengan baik jika Anda ingin menukar dua argumen yang dipisahkan oleh koma (bagian tata bahasa ini cukup sederhana), jadi saya kira akan mungkin untuk mengkonfigurasi SMIE dan menggunakannya untuk forward-sexp-functiontetapi Anda harus untuk menambahkan beberapa kode hanya menggunakan SMIE forward-sexp-function dalam kasus-kasus di mana ia bekerja dengan cukup baik, karena dalam banyak kasus lain itu akan mengakibatkan perilaku yang membingungkan.
Stefan