Как перенести два аргумента функции в 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.