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

84

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

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

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

cnu
источник

Ответы:

25

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

Марсель Леви
источник
К концу 2013 года tidy.el Версия: 20111222.1756 не может работать на Emacs 24 сwrong type argument: stringp, nil
keiw
@keiw Вероятно, потому что вы делаете это в буфере, у которого нет имени файла. Получил ту же ошибку и проследил ее, по крайней мере, на моей стороне.
Alf
110

Вам даже не нужно писать свою собственную функцию - sgml-mode (основной модуль gnu emacs) имеет встроенную функцию красивой печати, называемую (sgml-pretty-print ...), которая принимает аргументы начала и конца области.

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

Хуан Гарсия
источник
1
(sgml-pretty-print (начало региона) (конец региона))
ScootyPuff
7
Я не уверен, как это sgml-modeмогло измениться со временем. Сегодня я вызван C-x C-f foo.xml, M-x sgml-mode, тогда M-x sgml-pretty-printи мой файл XML был довольно распечатан. (Ну, emacs зависал на двадцать секунд или более перед завершением. Это был однострочный файл перед красивым отпечатком и 720 строк после него.)
daveloyall
1
Собственно, мне тоже пришлось сделать, C-x gчтобы выделить весь буфер как область.
daveloyall
3
Мне даже не пришлось переходить в sgml-режим. Это была команда Mx в режиме nXML!
nroose 04
1
Используя Emacs 26.2, я могу оставаться в режиме nXML, выбирать весь буфер C-x hи затем M-x sgml-pretty-print. Теперь xml будет хорошо отформатирован
Сведгин,
87

Если вам нужен только хороший отступ без введения каких-либо новых разрывов строки, вы можете применить 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.

Кристиан Берг
источник
1
Хорошее определение, спасибо. Удаление (nxml-mode) из приведенного выше defun-файла pretty-print позволяет ему работать в sgml-режиме, который встроен в emacs 22.2.1. Но я изменил его, чтобы сделать весь буфер (точка-мин) на (точка-макс), потому что это мое главное. Также есть одна ошибка: для каждой новой строки, которую вы вставляете, вам нужно будет увеличивать конец.
Cheeso 03
Как я могу использовать эту функцию в Emacs? Я скопировал и вставил код функции в рабочий буфер и оценил его. Как мне вызвать эту функцию?
Александр Радемейкер
1
После оценки функции defun вы можете вызвать ее как любую другую функцию: Mx bf-pretty-print-xml-region. (Вам, конечно, не нужно вводить все это, используйте завершение табуляции: Mx bf <tab> должно быть достаточно.) Вероятно, вы не хотите определять функцию каждый раз, когда хотите ее использовать, поэтому поместите ее где-нибудь где он загружается во время запуска, например, в ~ / .emacs.d / init.el
Christian Berg
1
Как насчет того, чтобы сломать длинные списки атрибутов?
ceve
Это потрясающе, потому что tidy жалуется на недопустимые кодировки символов и просит меня очистить их, прежде чем он переформатирует файл! Иногда дело в том, чтобы увидеть структуру неработающего xml файла, и приборка отказывается помочь.
TauPan
35

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

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

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

Тим Хелмстедт
источник
Используйте Mx mark-all-buffer впереди, чтобы пометить все содержимое буфера как область для обработки.
Харальд
19

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

(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)))

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

бубак
источник
2
Это дало мне ошибку в GNU Emacs 24, поэтому я изменил последнюю строку на:(indent-region 0 (count-lines (point-min) (point-max)))
John J. Camilleri
19

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

M-x sgml-mode
M-x sgml-pretty-print
Talespin_Kit
источник
8

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

  • в 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!"))
Джейсон Вирс
источник
5

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

<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=

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

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

HTH

Дэйв
источник
2

Я взял версию Джейсона Вирса и добавил логику, позволяющую помещать объявления 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!"))
Чизо
источник
1

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

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

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

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

cnu
источник
1

Использую 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))
)
Ярекчек
источник
1

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

M-x spacemacs/indent-region-or-buffer
JohnnyZ
источник
1

с 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/

тупица
источник
0

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


источник