Recolocar automaticamente o código elisp ao adicionar ou remover o código antes de um bloco recuado

18

Digamos que eu tenha um código elisp como:

(+ 2 3▮(+ 3
          4))

Existe uma maneira de recuar automaticamente o sexp após o cursor, quando adiciono ou removo símbolos?

Então, depois de pressionar SPC 4 SPC, eu obteria automaticamente:

(+ 2 3 4 ▮(+ 3
             4))

Eu posso fazer este manual chamando mark-sexpseguido por indent-region. Existem maneiras melhores de fazer isso?

Maciej Goszczycki
fonte
Não acho que haja combinações de teclas padrão convenientes, mas você mesmo pode criar algumas.
shosti 23/09/14
Eu sei, mas estou me perguntando especificamente, existem maneiras melhores de fazer isso. Por exemplo, como electric-indent-modeé muito melhor do que o mapeamento <return>paranewline-and-indent
Maciej Goszczycki

Respostas:

14

Em vez de mark-sexp+ indent-region, você pode pressionar C-M-q. Isso vai ligar indent-pp-sexp. Não é automático, mas é um pouco melhor do que ter que chamar dois comandos.

Ou, se você estiver usando paredit-mode, pressione M-q. Isso reindentará toda a definição de função em que você está.

Dmitry
fonte
1
(add-hook 'post-self-insert-hook 'indent-pp-sexp)funciona surpreendentemente bem.
Maciej Goszczycki 23/09
Parece bom. Isso pode ser caro em alguns modos, mas é uma boa abordagem.
Dmitry
Para usuários do Smartparens, você pode reinserir o atual desunião M-x sp-indent-defun. Eu vinculo isso a C-M-q.
Radon Rosborough
15

Modo de avanço agressivo

Como algumas pessoas pediram, eu transformei essa resposta em um pacote .

Se você tiver o Melpa configurado, poderá instalá-lo com

M-x package-install RET aggressive-indent

Consulte o Leia-me para obter todas as opções, mas a maneira mais simples de ativá-lo é:

(add-hook 'emacs-lisp-mode-hook #'aggressive-indent-mode)

A resposta antiga

A seguir, a indentação automática apenas nos buffers elisp. Tem a vantagem de também funcionar quando você apaga ou arranca coisas (em vez de apenas digitar). Também é fácil adicionar a outros modos.

Esta função irá indentar qualquer expressão s que o ponto esteja atualmente dentro. Você pode vinculá-lo a uma chave, se desejar, mas veja abaixo primeiro.

(require 'cl-lib)

(defun endless/indent-defun ()
  "Indent current defun.
Do nothing if mark is active (to avoid deactivating it), or if
buffer is not modified (to avoid creating accidental
modifications)."
  (interactive)
  (ignore-errors
    (unless (or (region-active-p)
                buffer-read-only
                (null (buffer-modified-p)))
      (let ((l (save-excursion (beginning-of-defun 1) (point)))
            (r (save-excursion (end-of-defun 1) (point))))
        (cl-letf (((symbol-function 'message) #'ignore))
          (indent-region l r))))))

Esse gancho fará com que essa função seja executada após você digitar qualquer coisa, mas apenas nos buffers elisp. Isso deve manter tudo sempre recuado.

(add-hook
 'emacs-lisp-mode-hook
 (lambda ()
   (add-hook 'post-command-hook
             #'endless/indent-defun nil 'local)))

Tente! Funciona notavelmente bem.

Além disso, seguindo a sugestão de @ holocronweaver nos comentários, você pode usar algo como o seguinte para idiomas semelhantes a c:

(define-key c++-mode-map ";"
  (lambda () (interactive)
    (insert ";")
    (endless/indent-defun)))
Malabarba
fonte
Seria excelente se essas mensagens silenciadas de recuo automático e idiomas melhor processados ​​terminassem as linhas com ponto e vírgula.
21914
@holocronweaver claro, eu posso tentar isso. Qual modo principal possui mensagens de recuo? E qual é o problema com ponto e vírgula?
Malabarba 28/09
Todos os modos que tentei no tronco do Emacs produziram mensagens de recuo, incluindo elisp. Como ponto e vírgula termina uma linha em vários idiomas, até você digitar o ponto e vírgula, a linha a seguir salta como se fizesse parte da linha atual.
21914
@holocronweaver Silenciou as mensagens. Obrigado por apontar isso.
Malabarba
Obrigado pela correção! Uma solução para o problema de ponto e vírgula é recuar apenas nos idiomas do modo c quando um ponto e vírgula é digitado.
holocronweaver
3

Não conheço uma solução pré-existente, mas você pode usar post-self-insert-hookpara fazer isso sozinho:

(defun my-indent-next-sexp ()
  (interactive)
  (mark-sexp)
  (indent-region))

(add-hook 'post-self-insert-hook 'my-indent-next-sexp)

Eu me preocuparia com possíveis problemas de desempenho.

shosti
fonte
1

Você pode experimentar o modo el-fly-indent , que é mais bem-comportado do que aggressive-indent-modequando se lida com o Elisp.

Jiahao
fonte