Jolie impression de fichiers XML sur Emacs

84

J'utilise emacs pour éditer mes fichiers xml (mode nxml) et les fichiers générés par la machine n'ont pas de joli formatage des balises.

J'ai cherché à imprimer le fichier entier avec indentation et à l'enregistrer, mais je n'ai pas pu trouver un moyen automatique.

Y a-t-il un moyen? Ou au moins un éditeur sur Linux qui peut le faire.

cnu
la source

Réponses:

25

J'utilise le mode nXML pour l' édition et Tidy quand je veux au format XML et HTML ou tiret. Il existe également une interface Emacs vers Tidy.

Marcel Levy
la source
À la fin de 2013, tidy.el Version: 20111222.1756 ne fonctionne pas sur Emacs 24 avecwrong type argument: stringp, nil
keiw
@keiw C'est probablement parce que vous le faites dans un tampon qui n'a pas de nom de fichier. J'ai eu la même erreur et je l'ai retracée de mon côté au moins.
Alf le
108

Vous n'avez même pas besoin d'écrire votre propre fonction - sgml-mode (un module principal de gnu emacs) a une fonction d'impression jolie intégrée appelée (sgml-pretty-print ...) qui prend les arguments de début et de fin de région.

Si vous coupez et collez du XML et que vous constatez que votre terminal coupe les lignes à des endroits arbitraires, vous pouvez utiliser cette jolie imprimante qui corrige d'abord les lignes brisées.

Juan Garcia
la source
1
(sgml-pretty-print (region-
begin
7
Je ne sais pas comment sgml-modecela a pu changer avec le temps. Aujourd'hui, j'invoquais C-x C-f foo.xml, M-x sgml-modepuis M-x sgml-pretty-printet mon fichier xml suis assez sérigraphiés. (Eh bien, emacs a été suspendu pendant vingt secondes ou plus avant de terminer. C'était un fichier d'une ligne avant la jolie copie et 720 lignes après.)
daveloyall
1
En fait, j'ai également dû faire C-x gpour sélectionner tout le tampon en tant que région.
daveloyall
3
Je n'ai même pas eu à passer en mode sgml. C'était une commande Mx en mode nXML!
nroose le
1
En utilisant Emacs 26.2, je peux rester en mode nXML, sélectionner tout le tampon C-x het ensuite M-x sgml-pretty-print. Le xml sera assez formaté maintenant
Swedgin
87

Si vous n'avez besoin que d'un joli retrait sans introduire de nouveaux sauts de ligne, vous pouvez appliquer la indent-regioncommande à l'ensemble du tampon avec ces frappes:

C-x h
C-M-\

Si vous avez également besoin d'introduire des sauts de ligne, de sorte que les balises d'ouverture et de fermeture soient sur des lignes séparées, vous pouvez utiliser la très belle fonction elisp suivante, écrite par Benjamin Ferrari . Je l'ai trouvé sur son blog et j'espère que je peux le reproduire ici:

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

Cela ne repose pas sur un outil externe comme Tidy.

Christian Berg
la source
1
Bon defun, merci. La suppression du (nxml-mode) de la jolie définition ci-dessus lui permet de fonctionner dans le mode sgml intégré à emacs 22.2.1. Mais je l'ai modifié pour faire le tampon entier (point-min) à (point-max) parce que c'est mon truc principal. Aussi, un bug: pour chaque nouvelle ligne que vous insérez, vous devrez incrémenter la fin.
Cheeso
Comment puis-je utiliser cette fonction dans Emacs? Je l' ai copié et collé le code de fonction dans zéro tampon et évalué il. Maintenant, comment appeler cette fonction?
Alexandre Rademaker
1
Après avoir évalué le defun, vous pouvez l'invoquer comme n'importe quelle autre fonction: Mx bf-pretty-print-xml-region. (Vous n'êtes pas obligé de tout taper, bien sûr, utilisez la complétion par tabulation: Mx bf <tab> devrait suffire.) Vous ne voulez probablement pas définir la fonction à chaque fois que vous voulez l'utiliser, alors mettez-la quelque part où il est chargé au moment du démarrage, par exemple dans ~ / .emacs.d / init.el
Christian Berg
1
Que diriez-vous de casser de longues listes d'attributs?
ceving le
C'est fabuleux, car tidy se plaint des encodages de caractères invalides et veut que je les nettoie avant de reformater le fichier! Parfois, le but est de voir la structure d'un fichier xml cassé et tidy refusera de vous aider.
TauPan
35

Emacs peut exécuter des commandes arbitraires avec M- |. Si vous avez installé xmllint:

"M- | xmllint --format -" formatera la région sélectionnée

"Cu M- | xmllint --format -" fera de même, en remplaçant la région par la sortie

Tim Helmstedt
la source
Utilisez Mx mark-whole-buffer devant pour marquer tout le contenu du tampon comme la région à traiter.
Harald
19

Merci à Tim Helmstedt ci-dessus, j'ai fait st comme ceci:

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

rapide et facile. Merci beaucoup.

bubak
la source
2
Cela m'a donné une erreur sur GNU Emacs 24, j'ai donc changé la dernière ligne en:(indent-region 0 (count-lines (point-min) (point-max)))
John J.Camilleri
19

Pour introduire des sauts de ligne puis une jolie impression

M-x sgml-mode
M-x sgml-pretty-print
Talespin_Kit
la source
8

voici quelques modifications que j'ai apportées à la version de Benjamin Ferrari:

  • le search-forward-regexpn'a pas spécifié de fin, donc il fonctionnerait sur des trucs du début de la région à la fin du tampon (au lieu de la fin de la région)
  • Maintenant augmente endcorrectement, comme l'a noté Cheeso.
  • il insérerait une rupture entre <tag></tag>, ce qui modifie sa valeur. Oui, techniquement, nous modifions les valeurs de tout ici, mais un début / une fin vide est beaucoup plus susceptible d'être significatif. Utilise maintenant deux recherches distinctes, légèrement plus strictes pour éviter cela.

A toujours le "ne se fie pas à un rangement externe", etc. Cependant, il en a besoin clpour la incfmacro.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 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!"))
Jason Viers
la source
5

Une façon de faire est si vous avez quelque chose au format ci-dessous

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

Dans Emacs, essayez

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

Cela indentera ci-dessus l'exemple xml en dessous

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

Dans VIM, vous pouvez le faire en

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

J'espère que cela t'aides.

utilisateur1028948
la source
2
  1. Emacs nxml-mode peut fonctionner sur le format présenté, mais vous devrez diviser les lignes.
  2. Pour des fichiers plus longs qui n'en valent tout simplement pas la peine. Exécutez cette feuille de style (idéalement avec Saxon dont IMHO obtient les retraits de ligne à peu près à droite) contre des fichiers plus longs pour obtenir une belle impression. Pour tous les éléments où vous souhaitez conserver un espace blanc, ajoutez leurs noms à côté de 'programlisting' comme dans 'programlisting yourElementName'

HTH

DaveP
la source
2

J'ai pris la version de Jason Viers et ajouté une logique pour mettre les déclarations xmlns sur leurs propres lignes. Cela suppose que vous avez xmlns = et xmlns: sans espace blanc intermédiaire.

(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!"))
Cheeso
la source
1

Tidy ressemble à un bon mode. Doit le regarder. Je vais l'utiliser si j'ai vraiment besoin de toutes les fonctionnalités qu'il offre.

Quoi qu'il en soit, ce problème me harcelait pendant environ une semaine et je ne cherchais pas correctement. Après avoir posté, j'ai commencé à chercher et j'ai trouvé un site avec une fonction elisp qui le fait très bien. L'auteur suggère également d'utiliser Tidy.

Merci pour la réponse Marcel (dommage que je n'ai pas assez de points pour vous upmod) .

Je publierai bientôt sur mon blog. Voici un article à ce sujet (avec un lien vers le site de Marcel).

cnu
la source
1

J'utilise xml-reformat-tagsdepuis xml-parse.el . Habituellement, vous voudrez avoir le point au début du fichier lors de l'exécution de cette commande.

Il est intéressant que le fichier soit incorporé dans Emacspeak . Quand j'utilisais Emacspeak au jour le jour, je pensais xml-reformat-tagsque c'était un intégré Emacs. Un jour, je l'ai perdu et j'ai dû faire une recherche sur Internet pour cela, et je suis donc entré dans la page wiki mentionnée ci-dessus.

J'attache également mon code pour démarrer xml-parse. Je ne sais pas si c'est le meilleur morceau de code Emacs, mais cela semble fonctionner pour moi.

(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))
)
Jarekczek
la source
1

Si vous utilisez spacemacs , utilisez simplement la commande 'spacemacs / indent-region-or-buffer'.

M-x spacemacs/indent-region-or-buffer
JohnnyZ
la source
1

à partir de 2017, emacs est déjà livré avec cette capacité par défaut, mais vous devez écrire cette petite fonction dans votre ~/.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))))

alors appelle juste M-x reformat-xml

source: https://davidcapello.com/blog/emacs/reformat-xml-on-emacs/

ninrod
la source
0

J'ai peur que j'aime beaucoup mieux la version de Benjamin Ferrari. La jolie impression interne place toujours la balise de fin dans une nouvelle ligne après la valeur, insérant CR indésirable dans les valeurs de balise.


la source