Досить друк XML-файлів на Emacs


84

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

Я шукав гарний друк цілого файлу з відступами та збереження, але не зміг знайти автоматичний спосіб.

Чи є спосіб? Або принаймні якийсь редактор на Linux, який може це зробити.

Відповіді:


25

Я використовую режим nXML для редагування та Tidy, коли хочу відформатувати та відступити XML або HTML. Існує також інтерфейс Emacs для Tidy.


До кінця 2013 року версія tidy.el: 20111222.1756 не працює на Emacs 24 зwrong type argument: stringp, nil
keiw

@keiw Це, мабуть, тому, що ви робите це в буфері, який не має імені файлу. Отримав ту саму помилку і простежив її щонайменше на моїй стороні.
Альф

108

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

Якщо ви вирізаєте та вставляєте xml і виявляєте, що ваш термінал рубає рядки в довільних місцях, ви можете використовувати цей гарний принтер, який спочатку виправляє ламані лінії.


1
(sgml-pretty-print (region-beginning) (region-end))
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

1
За допомогою Emacs 26.2 я можу залишатися в режимі nXML, вибрати весь буфер, C-x hа потім M-x sgml-pretty-print. Зараз XML буде досить відформатованим
Swedgin,

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
Хороший defun, дякую. Видалення (nxml-режиму) із вищезазначеного роздрукування досить-друку дозволяє йому працювати в sgml-режимі, вбудованому в emacs 22.2.1. Але я змінив його, щоб зробити весь буфер (point-min) до (point-max), тому що це моє головне. Також одна помилка: для кожного нового рядка, який ви вставляєте, вам потрібно буде збільшувати кінець.
Cheeso

Як я можу використовувати цю функцію в Emacs? Я скопіював і вставив код функції в скретч- буфер і оцінив його. Тепер, як мені викликати цю функцію?
Alexandre Rademaker

1
Оцінивши defun, ви можете викликати його як будь-яку іншу функцію: Mx bf-pretty-print-xml-region. (Вам не потрібно вводити все це, звичайно, використовуйте заповнення вкладки: Mx bf <tab> має бути достатньо.) Ви, мабуть, не хочете визначати функцію кожного разу, коли ви хочете її використовувати, тому помістіть її десь де він завантажується під час запуску, наприклад, у ~ / .emacs.d / init.el
Крістіан Берг

1
Як щодо розбиття довгих списків атрибутів?
витягування

Це казково, тому що охайний скаржиться на недійсні кодування символів і хоче, щоб я їх очистив, перш ніж він переформатує файл! Іноді суть полягає в тому, щоб побачити структуру зламаного XML-файлу, і Охайний відмовиться допомогти.
ТауПан

35

Emacs може запускати довільні команди з M- |. Якщо у вас встановлений xmllint:

"M- | xmllint --format -" відформатує вибраний регіон

"Cu M- | xmllint --format -" зробить те саме, замінивши область на вихід


Використовуйте Mx mark-whole-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)))
Джон Дж. Камілері

19

Для введення розривів рядків, а потім гарного друку

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

8

ось декілька налаштувань, які я зробив у версії Бенджаміна Феррарі:

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

Сподіваюся, це допомагає.


2
  1. Режим Emacs nxml може працювати у представленому форматі, але вам доведеться розділити рядки.
  2. Для довших файлів це просто не варто. Запустіть цю таблицю стилів (в ідеалі з саксонською, яка IMHO отримує відступи рядків праворуч) на довших файлах, щоб отримати гарний гарний принт. Для будь-яких елементів, де ви хочете зберегти пробіли, додайте їх імена поряд із "списком програм", як у "списком програм 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

Tidy виглядає як хороший режим. Потрібно на це подивитися. Буду використовувати, якщо мені дійсно потрібні всі функції, які він пропонує.

У всякому разі, ця проблема мене мучила близько тижня, і я не шукав належним чином. Після публікації я розпочав пошук і знайшов один сайт з функцією elisp, що робить це досить добре. Автор також пропонує використовувати Tidy.

Дякую за відповідь Марсель (дуже погано, у мене недостатньо балів, щоб модернізувати вас) .

Незабаром розміщу про це у своєму блозі. Ось допис про це (із посиланням на сайт Марселя).


1

Я використовую xml-reformat-tagsз xml-parse.el . Зазвичай вам потрібно мати точку на початку файлу під час запуску цієї команди.

Цікаво, що файл включено до Emacspeak . Коли я використовував Emacspeak щодня, я думавxml-reformat-tags , що це вбудований Emacs. Одного разу я його загубив, і мені довелося здійснити пошук в Інтернеті, і таким чином я увійшов на згадану вище сторінку wiki.

Я також додаю свій код для запуску 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

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 у значення тегу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.