Как перенести два аргумента функции в Python?


11

Как я могу поменять два аргумента при вызове функции Python?

Если я поставлю pointпространство между этими двумя аргументами:

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

и тогда M-t( transpose-words) я получаю:

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

С другой стороны, с CMt ( transpose-sexps) я получаю:

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

Что я хочу это:

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

Есть команда, которая сделает это?


2
Я не пробовал, но Anchored Transpose может быть использован для этого; это, однако, двухэтапный процесс.
Каушал Моди

Существует базовая функция, transpose-subrкоторая вызывает forwardфункцию и переводит ее в transposeфункцию. Поэтому, если бы у нас была c-forward-arglist(функция для перехода от одной функции arg к следующей - AFAICT, этого не существует), мы бы получили c-transpose-arglist.
Брендан,

Ответы:


4

Это то, что я тоже хотел иметь в течение долгого времени, и здесь я нашел некоторую мотивацию для работы над этим. Это, вероятно, не очень надежно, но с первой попытки это, кажется, покрывает случаи, которые я пытался:

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

Я получаю это, когда точка находится в пространстве (работает, когда на запятой): let *: Аргумент
неверного

@CroadLangshan исправить было относительно легко (см. Выше). Забавно, что я попытался последовать совету Стефана, написав альтернативу forward-sexp-function, и это оказалось слишком громоздким из-за запятых. Затем я попытался подражать, traspose-sexpчтобы сделать то же самое, и, опять же, необходимость учитывать запятые делает это действительно сложно. Я не утверждаю, что это всегда делает правильно, когда дело доходит до разделителей списков, может быть, только половину времени ...
wvxvw

Это не работает, (aa, bb)когда курсор находится на первом a. Это также не работает для транспонирования. FOO(aaa *, bbb, ccc)Как- *то портит транспонирование.
ideasman42

Также не удается FOO(&aaa, bbb)(и не меняет местами).
ideasman42

4

Я использую вариант, transpose-sexpsкоторый ищет случай, который вы описываете, и переносит вещи через запятую, или просто делает обычные transpose-sexps. Он также оставляет курсор на месте, а не перетаскивает его вперед, что немного отличается, но мне лично нравится.

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

Для меня это оставляет пробелы в начале вызова assertEqual (начиная с точки на пространстве).
Croad Langshan

1
Это не работает для аргументов ключевых слов, таких как: f(a=1, b=2)(оно переносится вокруг)
Att Righ

1
В то время как вопрос не является кодом C, этот ответ работает для f(a=1, b=2)- emacs.stackexchange.com/a/47930/2418
ideasman42

2

В режимах, которые используют SMIE, transpose-sexpдолжен корректно работать для этого случая. Они по-прежнему не будут работать, когда символ инфикса (он же «разделитель») не является ,(или ;), а является словом (например and).

Итак, я считаю, что команда для этого есть, transpose-sexpи когда она не работает правильно, я рассматриваю это как ошибку (но ошибка, которая может быть сложной и / или требует времени для исправления и имеет низкий приоритет, поэтому не задержи дыхание). Способ исправить это - установить forward-sexp-functionфункцию, которая будет знать, что «после того, как я вижу запятую, я просто перебираю весь аргумент».


1
Я предполагаю, что java-режим (например) не использует SMIE? Как можно это исправить?
Сэмюэль Эдвин Уорд

1
Нет, действительно, основные режимы для C-подобных языков не используют SMIE и не только по историческим причинам: синтаксический анализатор SMIE слишком слаб для этих языков. При этом парсер SMIE будет работать нормально, если вы хотите поменять местами два аргумента, разделенных запятой (эта часть грамматики достаточно проста), поэтому я думаю, что можно было бы настроить SMIE и использовать его для, forward-sexp-functionно вы бы добавить некоторый код, чтобы использовать SMIE только forward-sexp-function в тех случаях, когда он работает достаточно хорошо, поскольку во многих других случаях это может привести к путанице в поведении.
Стефан
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.