Bastante impresión de archivos XML en Emacs


84

Utilizo emacs para editar mis archivos xml (modo nxml) y los archivos generados por la máquina no tienen un formato bonito de las etiquetas.

He buscado bastante imprimir todo el archivo con sangría y guardarlo, pero no pude encontrar una forma automática.

¿Hay alguna manera? O al menos algún editor en Linux que pueda hacerlo.

Respuestas:


25

Yo uso el modo nxml para la edición y ordenado cuando quiero formato y XML o HTML guión. También hay una interfaz Emacs para Tidy.


A finales de 2013, tidy.el Versión: 20111222.1756 no se ejecuta en Emacs 24 conwrong type argument: stringp, nil
keiw

@keiw Eso es probablemente porque lo estás haciendo en un búfer que no tiene un nombre de archivo. Recibí el mismo error y lo rastreé al menos de mi lado.
Alf

108

Ni siquiera necesita escribir su propia función: sgml-mode (un módulo principal de gnu emacs) tiene una función de impresión bonita incorporada llamada (sgml-pretty-print ...) que toma los argumentos de inicio y final de la región.

Si está cortando y pegando xml y encuentra que su terminal está cortando las líneas en lugares arbitrarios, puede usar esta bonita impresora que corrige las líneas discontinuas primero.


1
(sgml-pretty-print (región-comienzo) (región-fin))
ScootyPuff

7
No estoy seguro de cómo sgml-modepudo haber cambiado con el tiempo. Hoy en día, invoqué C-x C-f foo.xml, M-x sgml-modey, a continuación M-x sgml-pretty-printy mi archivo xml puso bastante impresa. (. Bueno, emacs colgados durante veinte segundos o más antes de completar Fue un archivo de una línea antes de la impresión bonita y después de 720 líneas.)
daveloyall

1
En realidad, también tuve que hacer C-x gpara seleccionar todo el búfer como región.
daveloyall

3
Ni siquiera tuve que cambiar al modo sgml. ¡Era un comando Mx en modo nXML!
nroose

1
Usando Emacs 26.2, puedo permanecer en modo nXML, seleccionar todo el búfer C-x hy luego M-x sgml-pretty-print. El xml estará bastante formateado ahora
Swedgin

87

Si solo necesita una sangría bastante sin introducir nuevos saltos de línea, puede aplicar el indent-regioncomando a todo el búfer con estas pulsaciones de teclas:

C-x h
C-M-\

Si también necesita introducir saltos de línea, de modo que las etiquetas de apertura y cierre estén en líneas separadas, puede usar la siguiente función elisp muy agradable, escrita por Benjamin Ferrari . Lo encontré en su blog y espero que esté bien para mí reproducirlo aquí:

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

Esto no depende de una herramienta externa como Tidy.


1
Bien defun, gracias. Eliminar el (nxml-mode) de la defun de pretty-print anterior le permite trabajar en el modo sgml que está integrado en emacs 22.2.1. Pero lo modifiqué para hacer todo el búfer (point-min) a (point-max) porque eso es lo principal. Además, un error: por cada nueva línea que inserte, deberá incrementar end.
Cheeso

¿Cómo puedo usar esta función en Emacs? He copiado y pegado el código de función en cero búfer y evaluado. Ahora, ¿cómo invoco esta función?
Alexandre Rademaker

1
Después de evaluar la defun, puede invocarla como cualquier otra función: Mx bf-pretty-print-xml-region. (No tiene que escribirlo todo, por supuesto, use la función de completar con tabulación: Mx bf <tab> debería ser suficiente). Probablemente no quiera definir la función cada vez que quiera usarla, así que póngala en algún lugar donde se carga a la hora de inicio, por ejemplo, en ~ / .emacs.d / init.el
Christian Berg

1
¿Qué tal romper largas listas de atributos?
hasta el

Esto es fabuloso, porque tidy se queja de codificaciones de caracteres no válidas y quiere que las limpie antes de reformatear el archivo. A veces, el punto es ver la estructura de un archivo xml roto y Tidy se negará a ayudar.
TauPan

35

Emacs puede ejecutar comandos arbitrarios con M- |. Si tiene xmllint instalado:

"M- | xmllint --format -" formateará la región seleccionada

"Cu M- | xmllint --format -" hará lo mismo, reemplazando la región con la salida


Utilice Mx mark-whole-buffer al frente para marcar todo el contenido del búfer como la región a procesar.
Harald

19

Gracias a Tim Helmstedt arriba hice st así:

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

rapido y facil. Muchas gracias.


2
Esto me dio un error en GNU Emacs 24, así que cambié la última línea a:(indent-region 0 (count-lines (point-min) (point-max)))
John J. Camilleri

19

Para introducir saltos de línea y luego una bonita impresión

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

8

aquí hay algunos ajustes que hice a la versión de Benjamin Ferrari:

  • el search-forward-regexpno especificó un final, por lo que operaría en cosas desde el principio de la región hasta el final del búfer (en lugar del final de la región)
  • Ahora aumenta endcorrectamente, como señaló Cheeso.
  • insertaría una ruptura entre <tag></tag>, lo que modifica su valor. Sí, técnicamente estamos modificando los valores de todo aquí, pero es mucho más probable que un inicio / final vacío sea significativo. Ahora usa dos búsquedas separadas, un poco más estrictas, para evitar eso.

Todavía tiene el "no depende del orden externo", etc. Sin embargo, sí lo requiere clpara 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!"))

5

Una forma de hacerlo es si tiene algo en el siguiente formato

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

En Emacs, intente

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

Esto sangrará el ejemplo xml anterior al siguiente

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

En VIM puede hacer esto

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

Espero que esto ayude.


2
  1. Emacs nxml-mode puede funcionar en el formato presentado, pero tendrá que dividir las líneas.
  2. Para archivos más largos que simplemente no valen la pena. Ejecute esta hoja de estilo (idealmente con Saxon, que en mi humilde opinión obtiene las sangrías de línea correctas) contra archivos más largos para obtener una impresión bonita y agradable. Para cualquier elemento en el que desee retener el espacio en blanco, agregue sus nombres junto con 'programlisting' como en 'programlisting yourElementName'

HTH


2

Tomé versión Jason Viers' y la lógica añadido para poner xmlns declaraciones en sus propias líneas. Esto supone que tiene xmlns = y xmlns: sin espacios en blanco intermedios.

(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 parece un buen modo. Debo mirarlo. Lo usaré si realmente necesito todas las funciones que ofrece.

De todos modos, este problema me estuvo molestando durante aproximadamente una semana y no estaba buscando correctamente. Después de publicar, comencé a buscar y encontré un sitio con una función elisp que lo hace bastante bien. El autor también sugiere usar Tidy.

Gracias por la respuesta Marcel (lástima que no tengo suficientes puntos para mejorarte) .

Publicaré sobre esto pronto en mi blog. Aquí hay una publicación al respecto (con un enlace al sitio de Marcel).


1

Yo uso xml-reformat-tagsde xml-parse.el . Por lo general, querrá tener el punto al principio del archivo cuando ejecute este comando.

Es interesante que el archivo esté incorporado en Emacspeak . Cuando estaba usando Emacspeak día a día, pensé que xml-reformat-tagsera un Emacs incorporado. Un día lo perdí y tuve que hacer una búsqueda en Internet para eso, y así ingresé a la página wiki mencionada anteriormente.

Adjunto también mi código para iniciar xml-parse. No estoy seguro de si esta es la mejor pieza de código de Emacs, pero parece funcionar para mí.

(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

Si usa spacemacs , simplemente use el comando 'spacemacs / indent-region-or-buffer'.

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

1

A partir de 2017, emacs ya viene con esta capacidad de forma predeterminada, pero debe escribir esta pequeña función en su ~/.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))))

entonces solo llama M-x reformat-xml

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


0

Me temo que me gusta mucho más la versión de Benjamin Ferrari. La impresión bonita interna siempre coloca la etiqueta final en una nueva línea después del valor, insertando CR no deseado en los valores de la etiqueta.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.