Como transpor dois argumentos de uma função em Python?

11

Como posso trocar dois argumentos em uma chamada para uma função Python?

Se eu colocar pointo espaço entre esses dois argumentos:

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

e então M-t( transpose-words), eu recebo:

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

Por outro lado, com CMt ( transpose-sexps) eu recebo:

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

O que eu quero é:

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

Existe um comando que fará isso?

Croad Langshan
fonte
2
Ainda não tentei, mas o Anchored Transpose pode ser usado para isso; é, porém, um processo de duas etapas.
Kaushal Modi
Existe uma função principal chamada transpose-subrque pega uma forwardfunção e a converte em uma transposefunção. Portanto, se tivéssemos c-forward-arglist(função para passar de uma função arg para a seguinte - AFAICT, isso não existe), teríamos c-transpose-arglist.
Brendan

Respostas:

4

Isso também é algo que eu queria ter há muito tempo, e aqui encontrei alguma motivação para trabalhar nisso. Provavelmente não é muito robusto, mas na primeira tentativa parece cobrir os casos que tentei:

(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
fonte
Eu recebo isso quando ponto é sobre o espaço (obras quando na vírgula): deixe *: argumento de tipo errado: integer-ou-marcador-p, nil
Croad Langshan
@CroadLangshan a correção foi relativamente fácil (veja acima). A coisa engraçada que tentei seguir o conselho de Stefan, escrevendo uma alternativa forward-sexp-function, e isso parecia muito complicado por causa das vírgulas. Tentei copiar o arquivo traspose-sexppara fazer a mesma coisa e, novamente, ter que explicar vírgulas torna isso realmente difícil. Eu não reivindico isso sempre faz a coisa certa quando se trata de lista delimitadores, talvez apenas metade do tempo ...
wvxvw
Isso falha (aa, bb)quando o cursor está no primeiro a. Isso também não funciona para a transposição FOO(aaa *, bbb, ccc)As *alguma forma messes-se de transposição.
ideasman42
Também falha em FOO(&aaa, bbb)(& não troca).
ideasman42
4

Eu uso uma variação transpose-sexpsque procura o caso que você descreve e transpõe as coisas separadas por vírgulas, ou apenas faz regular transpose-sexps. Ele também deixa o cursor no lugar em vez de arrastá-lo para frente, o que é um pouco diferente, mas eu pessoalmente gosto.

(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
fonte
Para mim, isso deixa um espaço em branco no início da chamada assertEqual (começando com ponto no espaço).
Croad Langshan
1
Isso não funciona para os argumentos de palavra-chave, como: f(a=1, b=2)(transpõe ao redor)
Att Righ
1
Enquanto a pergunta é sobre o código C, esta resposta funciona para f(a=1, b=2)- emacs.stackexchange.com/a/47930/2418
ideasman42
2

Nos modos que usam o SMIE, transpose-sexpdeve funcionar corretamente para esse caso. Eles ainda falharão quando o símbolo do infix (também conhecido como "separador") não é um ,(ou a ;), mas é uma palavra (por exemplo and).

Portanto, minha opinião é que o comando para isso é transpose-sexpe, quando isso não funcionar corretamente, considero-o um bug (mas um bug que pode ser difícil e / ou levar tempo para corrigir e ter baixa prioridade, então não ' prenda a respiração). A maneira de corrigi-lo é definindo forward-sexp-functionuma função que saiba que "depois de ver uma vírgula, apenas pulo o argumento inteiro".

Stefan
fonte
1
Eu acho que o modo java (por exemplo) não usa o SMIE? Como alguém resolveria isso?
Samuel Edwin Ward
1
Não, de fato, os principais modos para idiomas do tipo C não usam o SMIE e não apenas por razões históricas: o analisador do SMIE é muito fraco para esses idiomas. Dito isto, o analisador do SMIE funcionaria bem se você quiser trocar dois argumentos separados por vírgula (essa parte da gramática é bastante simples), então acho que seria possível configurar o SMIE e usá-lo, forward-sexp-functionmas você teria adicionar algum código para usar apenas o SMIE forward-sexp-function nos casos em que funciona bem o suficiente, pois em muitos outros casos isso resultaria em comportamento confuso.
Stefan