Есть ли в Emacs пакет с автоматическим выравниванием или режим?

15

Иногда я редактирую файлы (например, манифесты Puppet), где я хочу выровнять содержимое буфера на лету. Например, я знаю, что могу использовать align-regexpили alignдля выравнивания кода следующим образом, если я его выбрал:

# M-x align-regexp "=>" will line everything up nicely 
file { "/etc/foo":
  ensure => present,
  mode => "0666",
  source => "puppet:///modules/example/foo",
}

Но я действительно хочу, чтобы это происходило на лету - без необходимости выбирать регион или весь буфер. Есть ли пакет или режим, который будет делать это, или это то, что должно быть встроено в интересующий меня режим?

Изменить: я спросил о Puppet и CFEngine, так как это то, что я кодирую в большинстве случаев, и у меня есть сильные предпочтения для выравнивания текста. Тем не менее, мне также было любопытно (и не очень хорошо выразил это) узнать, является ли выравнивание «на лету» функцией, характерной для режимов Emacs (в основном в режимах для языков программирования), или большинство людей выравнивают при сохранении или при некоторых другой момент.

Святой Аардварк Ковровое покрытие
источник
Существует расширенный регион , который полезен для выбора последовательно больших областей.
Бастиб
1
Можете ли вы описать больше того, что вы обычно выравниваете? Это просто знаки равенства и в кукольном режиме? Вы можете изменить код в этом ответе Emacs SE для автоматического отступа @Malabarba, чтобы получить то, что вы хотите. Просто замените indent-regionна align-to-equalsи определите эту функцию как (defun align-to-equals (begin end) (interactive "r") (align-regexp begin end "\\(\\s-*\\)=" 1 1 nil)); и emacs-lisp-mode-hookс puppet-mode-hook.
Каушал Моди

Ответы:

5

Если вы хотите пометить область для автоматического выравнивания, то, вероятно, не так уж сложно сделать что-то вроде:

(require 'cl-lib)

(defvar my--auto-align-overlays nil)
(make-variable-buffer-local 'my--auto-align-overlays)

(defun my-auto-align (beg end regexp)
  (interactive "r\nsRegexp: ")
  (let ((ol (make-overlay beg end)))
    (overlay-put ol 'evaporate t)
    (overlay-put ol 'my--auto-align-regexp regexp)
    (overlay-put ol 'modification-hooks '(my--auto-align-function))
    (push ol my--auto-align-overlays)
    (add-hook 'post-command-hook 'my--auto-align-pch nil t)))

(defun my--auto-align-function (ol &rest _args)
  (cl-pushnew ol my--auto-align-overlays)
  (add-hook 'post-command-hook 'my--auto-align-pch nil t))

(defun my--auto-align-pch ()
  (dolist (ol my--auto-align-overlays)
    (align-regexp (overlay-start ol) (overlay-end ol)
                  (concat "\\(\\s-*\\)" (overlay-get ol 'my--auto-align-regexp))
                  1 align-default-spacing))
  (setq my--auto-align-overlays nil)
  (remove-hook 'post-command-hook 'my--auto-align-pch))

Но если вы хотите, чтобы он был полностью автоматическим, вам нужно было бы найти способ охарактеризовать, какие области вы хотите автоматически выровнять (и с какими регулярными выражениями), что более сложно. Я думаю, вы могли бы предоставить несколько регулярных выражений, а затем просканировать буфер на наличие последовательных строк, соответствующих одному из этих регулярных выражений.

Стефан
источник
Раньше я не использовал оверлеи, но в руководстве Elisp написано, что они предназначены для изменения внешнего вида - правильно ли я это понял? Если так, значит ли это, что этот ответ только изменяет внешний вид буфера, а не фактическое содержимое файла после его сохранения? (Это последнее, что я искал, но изменение внешнего вида было бы интересным вариантом.)
Святой Аардвар, Ковровое покрытие,
2
@SaintAardvarktheCarpeted Оверлеи обычно используются для изменения внешнего вида, но их применимость выходит за рамки этого. В этом ответе (IIUC) наложения используются для маркировки областей, которые должны быть выровнены (поэтому буфер действительно изменяется, а не только внешний вид). Функция my-auto-alignиспользуется для определения этих наложений (отметьте области, которые вы хотите выровнять). Причина (я думаю), что Стефан преодолевает все эти проблемы, заключается в том, что (в отличие от отступа), если вы просто попытаетесь выровнять весь буфер, вы получите нежелательные результаты, поэтому вам нужно выровнять фрагменты за раз.
Малабарба
3

Кукольный режим обеспечивает puppet-align-block. Вы можете организовать автоматический вызов этой функции, например, добавив ее в post-command-hookлокально (не проверено):

(defun puppet-auto-align ()
  (unless (puppet-in-string-or-comment-p)
    (puppet-align-block)))

(define-minor-mode puppet-auto-align-mode
  :init-value nil
  :keymap nil
  :lighter "PAl"
  :group 'puppet-lint
  (if puppet-auto-align-mode
      (add-hook 'post-command-hook #'puppet-auto-align nil 'local)
    (remove-hook 'post-command-hook #'puppet-auto-align 'local)))

(add-hook 'puppet-mode-hook #'puppet-auto-align-mode)

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

Поэтому, выступая в роли сопровождающего Puppet Mode, я рекомендую против этого, и мы не поможем вам и не примем сообщения об ошибках, если вы используете этот код.

Кроме того, для больших блоков ресурсов это может быть медленным, поэтому вы, вероятно, захотите использовать таймер простоя вместо post-command-hookпрямого.

lunaryorn
источник