Хорошая печать XML-файлов на Emacs

Я использую emacs для редактирования моих файлов xml (режим nxml), и файлы, созданные машиной, не имеют красивого форматирования тегов.

Я искал, чтобы распечатать весь файл с отступом и сохранить его, но не смог найти автоматический способ.

Есть способ? Или, по крайней мере, какой-нибудь редактор на Linux, который может это сделать.

Ответов (15)

Решение

Я использую режим nXML для редактирования и Tidy, когда хочу отформатировать XML или HTML и сделать отступ. Также существует интерфейс Emacs для Tidy.

Один из способов сделать это: если у вас есть что-то в формате ниже

<abc>     <abc><abc>   <abc></abc> </abc></abc>       </abc>

В Emacs попробуйте

M-x nxml-mode
M-x replace-regexp RET  > *< RET >C-q C-j< RET 
C-M-\ to indent

Это приведет к отступу от примера xml до ниже

<abc>
  <abc>
    <abc>
      <abc>
      </abc>
    </abc>
  </abc>
</abc>

В VIM это можно сделать с помощью

:set ft=xml
:%s/>\s*</>\r</g
ggVG=

Надеюсь это поможет.

Боюсь, мне гораздо больше нравится версия Бенджамина Феррари. Внутренняя симпатичная печать всегда помещает конечный тег в новую строку после значения, вставляя нежелательные CR в значения тегов.

Благодаря Тиму Хелмстедту я сделал это так:

(defun nxml-pretty-format ()
    (interactive)
    (save-excursion
        (shell-command-on-region (point-min) (point-max) "xmllint --format -" (buffer-name) t)
        (nxml-mode)
        (indent-region begin end)))

быстро и легко. Большое спасибо.

Я взял версию Джейсона Вирса и добавил логику, позволяющую помещать объявления xmlns в отдельные строки. Это предполагает, что у вас есть xmlns = и xmlns: без промежуточных пробелов.

(defun cheeso-pretty-print-xml-region (begin end)
  "Pretty format XML markup in region. You need to have nxml-mode
http://www.emacswiki.org/cgi-bin/wiki/NxmlMode installed to do
this.  The function inserts linebreaks to separate tags that have
nothing but whitespace between them.  It then indents the markup
by using nxml's indentation rules."
  (interactive "r")
  (save-excursion
    (nxml-mode)
    ;; split <foo><bar> or </foo><bar>, but not <foo></foo>
    (goto-char begin)
    (while (search-forward-regexp ">[ \t]*<[^/]" end t)
      (backward-char 2) (insert "\n") (incf end))
    ;; split <foo/></foo> and </foo></foo>
    (goto-char begin)
    (while (search-forward-regexp "<.*?/.*?>[ \t]*<" end t)
      (backward-char) (insert "\n") (incf end))
    ;; put xml namespace decls on newline
    (goto-char begin)
    (while (search-forward-regexp "\\(<\\([a-zA-Z][-:A-Za-z0-9]*\\)\\|['\"]\\) \\(xmlns[=:]\\)" end t)
      (goto-char (match-end 0))
      (backward-char 6) (insert "\n") (incf end))
    (indent-region begin end nil)
    (normal-mode))
  (message "All indented!"))

Использую xml-reformat-tagsиз xml-parse.el . Обычно при выполнении этой команды вы хотите, чтобы точка была в начале файла.

Интересно, что файл включен в Emacspeak . Когда я ежедневно использовал Emacspeak, я думал, xml-reformat-tags что это встроенный Emacs. Однажды я потерял его, и мне пришлось искать его в Интернете, и я попал на упомянутую выше вики-страницу.

Прилагаю также свой код для запуска xml-parse. Не уверен, что это лучший фрагмент кода Emacs, но мне кажется, он работает.

(if (file-exists-p "~/.emacs.d/packages/xml-parse.el")
  (let ((load-path load-path))
    (add-to-list 'load-path "~/.emacs.d/packages")
    (require 'xml-parse))
)

Для введения разрывов строк и затем красивой печати

M-x sgml-mode
M-x sgml-pretty-print

Если вы используете spacemacs , просто используйте команду spacemacs / indent-region-or-buffer.

M-x spacemacs/indent-region-or-buffer

с 2017 года emacs уже имеет эту возможность по умолчанию, но вы должны записать эту небольшую функцию в свой ~/.emacs.d/init.el :

(require 'sgml-mode)

(defun reformat-xml ()
  (interactive)
  (save-excursion
    (sgml-pretty-print (point-min) (point-max))
    (indent-region (point-min) (point-max))))

тогда просто позвони M-x reformat-xml

источник: https://davidcapello.com/blog/emacs/reformat-xml-on-emacs/

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

Во всяком случае, эта проблема не давала мне покоя около недели, и я не искал как следует. После публикации я начал поиск и нашел один сайт с функцией elisp, которая делает это довольно хорошо. Также автор предлагает использовать Tidy.

Спасибо за ответ, Марсель (жаль, что у меня недостаточно очков, чтобы улучшить тебя) .

Скоро напишу об этом в моем блоге. Вот пост об этом (со ссылкой на сайт Марселя).

  1. Emacs nxml-mode может работать с представленным форматом, но вам придется разделить строки.
  2. Для более длинных файлов это просто не стоит. Запустите эту таблицу стилей (в идеале с Saxon, который, IMHO, получает правильные отступы строк) для более длинных файлов, чтобы получить красивую красивую печать. Для любых элементов, в которых вы хотите сохранить пробелы, добавьте их имена вместе с 'programlisting', как в 'programlisting yourElementName'

HTH

Если вам нужен только хороший отступ без введения каких-либо новых разрывов строки, вы можете применить indent-region команду ко всему буферу с помощью следующих нажатий клавиш:

C-x h
C-M-\

Если вам также необходимо ввести разрывы строк, чтобы открывающие и закрывающие теги располагались на отдельных строках, вы можете использовать следующую очень красивую функцию elisp, написанную Бенджамином Феррари . Я нашел это в его блоге и надеюсь, что смогу воспроизвести его здесь:

(defun bf-pretty-print-xml-region (begin end)
  "Pretty format XML markup in region. You need to have nxml-mode
http://www.emacswiki.org/cgi-bin/wiki/NxmlMode installed to do
this.  The function inserts linebreaks to separate tags that have
nothing but whitespace between them.  It then indents the markup
by using nxml's indentation rules."
  (interactive "r")
  (save-excursion
    (nxml-mode)
    (goto-char begin)
    (while (search-forward-regexp "\>[ \\t]*\<" nil t) 
      (backward-char) (insert "\n") (setq end (1+ end)))
    (indent-region begin end))
  (message "Ah, much better!"))

Это не зависит от внешнего инструмента, такого как Tidy.

You don't even need to write your own function - sgml-mode (a gnu emacs core module) has a built-in pretty printing function called (sgml-pretty-print ...) which takes region beginning and end arguments.

Если вы вырезаете и вставляете xml и обнаруживаете, что ваш терминал обрезает строки в произвольных местах, вы можете использовать этот симпатичный принтер, который сначала исправляет ломаные линии.

Emacs может запускать произвольные команды с помощью M- |. Если у вас установлен xmllint:

«M- | xmllint --format -» отформатирует выбранный регион.

«Cu M- | xmllint --format -» будет делать то же самое, заменяя регион выходным

вот несколько настроек, которые я внес в версию Бенджамина Феррари:

  • search-forward-regexpне указали конец, поэтому он будет работать на вещи , от начала региона до конца буфера (вместо конца области)
  • endКак заметил Чизо, теперь увеличивается правильно.
  • между ними будет вставлен разрыв <tag></tag>, что изменит его значение. Да, технически мы здесь изменяем значения всего, но пустое начало / конец гораздо более вероятно будет иметь значение. Теперь использует два отдельных, немного более строгих поиска, чтобы этого избежать.

По-прежнему есть «не полагается на внешнюю приборку» и т. Д. Однако cl для incf макроса это действительно необходимо .

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; pretty print xml region
(defun pretty-print-xml-region (begin end)
  "Pretty format XML markup in region. You need to have nxml-mode
http://www.emacswiki.org/cgi-bin/wiki/NxmlMode installed to do
this.  The function inserts linebreaks to separate tags that have
nothing but whitespace between them.  It then indents the markup
by using nxml's indentation rules."
  (interactive "r")
  (save-excursion
    (nxml-mode)
    (goto-char begin)
    ;; split <foo><foo> or </foo><foo>, but not <foo></foo>
    (while (search-forward-regexp ">[ \t]*<[^/]" end t)
      (backward-char 2) (insert "\n") (incf end))
    ;; split <foo/></foo> and </foo></foo>
    (goto-char begin)
    (while (search-forward-regexp "<.*?/.*?>[ \t]*<" end t)
      (backward-char) (insert "\n") (incf end))
    (indent-region begin end nil)
    (normal-mode))
  (message "All indented!"))