Piuttosto stampare file XML su Emacs


84

Uso emacs per modificare i miei file xml (modalità nxml) ei file sono stati generati dalla macchina non hanno alcuna formattazione carina dei tag.

Ho cercato di stampare abbastanza l'intero file con rientro e salvarlo, ma non sono riuscito a trovare un modo automatico.

C'è un modo? O almeno qualche editor su Linux che può farlo.

Risposte:


25

Uso la modalità nXML per l'editing e Tidy quando voglio formattare e indentare XML o HTML. C'è anche un'interfaccia Emacs per Tidy.


Entro la fine del 2013 tidy.el Versione: 20111222.1756 non riesce a funzionare su Emacs 24 conwrong type argument: stringp, nil
keiw

@keiw Probabilmente è perché lo stai facendo in un buffer che non ha un nome di file. Ho avuto lo stesso errore e l'ho fatto risalire a quello almeno dalla mia parte.
Alf

110

Non hai nemmeno bisogno di scrivere la tua funzione - sgml-mode (un modulo principale di gnu emacs) ha una funzione di stampa graziosa incorporata chiamata (sgml-pretty-print ...) che accetta gli argomenti di inizio e fine della regione.

Se stai tagliando e incollando xml e trovi che il tuo terminale sta tagliando le linee in posti arbitrari, puoi usare questa bella stampante che corregge prima le linee interrotte.


1
(sgml-pretty-print (region-
start

7
Non sono sicuro di come sgml-modepotrebbe essere cambiato nel tempo. Oggi, ho invocato C-x C-f foo.xml, M-x sgml-modee poi M-x sgml-pretty-printe il mio file xml ottenuto abbastanza stampati. (Bene, emacs è rimasto impiccato per venti secondi o più prima di essere completato. Era un file di una riga prima della bella stampa e 720 righe dopo.)
daveloyall

1
In realtà, dovevo anche C-x gselezionare l'intero buffer come regione.
daveloyall

3
Non ho nemmeno dovuto passare alla modalità sgml. Era un comando Mx in modalità nXML!
nroose

1
Usando Emacs 26.2, posso rimanere in modalità nXML, selezionare l'intero buffer C-x he poi M-x sgml-pretty-print. L'xml ora sarà abbastanza formattato
Swedgin

87

Se hai solo bisogno di un bel rientro senza introdurre nuove interruzioni di riga, puoi applicare il indent-regioncomando all'intero buffer con queste sequenze di tasti:

C-x h
C-M-\

Se hai anche bisogno di introdurre interruzioni di riga, in modo che i tag di apertura e chiusura siano su righe separate, potresti usare la seguente funzione elisp molto carina, scritta da Benjamin Ferrari . L'ho trovato sul suo blog e spero che mi vada bene riprodurlo qui:

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

Questo non si basa su uno strumento esterno come Tidy.


1
Buon defun, grazie. Rimuovere la (modalità nxml) dal defun pretty-print sopra gli permette di lavorare nella modalità sgml che è incorporata in emacs 22.2.1. Ma l'ho modificato per fare l'intero buffer (point-min) a (point-max) perché questa è la mia cosa principale. Inoltre, un bug: per ogni nuova riga che inserisci, dovrai incrementare la fine.
Cheeso

Come posso usare questa funzione in Emacs? Ho copiato e incollato il codice della funzione nel buffer di memoria virtuale e l'ho valutato. Ora, come invoco questa funzione?
Alexandre Rademaker

1
Dopo aver valutato il defun, puoi richiamarlo come qualsiasi altra funzione: Mx bf-pretty-print-xml-region. (Non devi digitare tutto, ovviamente, usa il completamento con tabulazione: Mx bf <tab> dovrebbe essere sufficiente.) Probabilmente non vuoi definire la funzione ogni volta che vuoi usarla, quindi mettila da qualche parte dove viene caricato all'avvio, ad esempio in ~ / .emacs.d / init.el
Christian Berg

1
Che ne dici di rompere lunghi elenchi di attributi?
Ceving

Questo è favoloso, perché tidy si lamenta di codifiche di caratteri non valide e vuole che le ripulisca prima di riformattare il file! A volte il punto è vedere la struttura di un file xml rotto e ordinato si rifiuterà di aiutare.
TauPan

35

Emacs può eseguire comandi arbitrari con M- |. Se hai xmllint installato:

"M- | xmllint --format -" formatterà la regione selezionata

"Cu M- | xmllint --format -" farà lo stesso, sostituendo la regione con l'output


Usa Mx mark-whole-buffer in front per contrassegnare l'intero contenuto del buffer come regione da elaborare.
Harald

19

Grazie a Tim Helmstedt sopra ho fatto st così:

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

veloce e facile. Grazie molto.


2
Questo mi ha dato un errore su GNU Emacs 24, quindi ho cambiato l'ultima riga in:(indent-region 0 (count-lines (point-min) (point-max)))
John J. Camilleri

19

Per introdurre interruzioni di riga e quindi stampare in modo carino

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

8

ecco alcune modifiche che ho apportato alla versione di Benjamin Ferrari:

  • il search-forward-regexpnon ha specificato una fine, quindi avrebbe operato su cose dall'inizio della regione alla fine del buffer (invece che alla fine della regione)
  • Ora aumenta endcorrettamente, come ha notato Cheeso.
  • inserirà un'interruzione tra <tag></tag>, che ne modifica il valore. Sì, tecnicamente stiamo modificando i valori di tutto qui, ma è molto più probabile che un inizio / fine vuoto sia significativo. Ora utilizza due ricerche separate, leggermente più rigorose per evitarlo.

Ha ancora "non si basa sull'ordinamento esterno", ecc. Tuttavia, richiede clla 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

Un modo per farlo è se hai qualcosa nel formato seguente

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

In Emacs, prova

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

Questo farà rientrare l'esempio xml sopra a sotto

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

In VIM puoi farlo da

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

Spero che sia di aiuto.


2
  1. La modalità nxml di Emacs può funzionare sul formato presentato, ma dovrai dividere le linee.
  2. Per file più lunghi che semplicemente non ne vale la pena. Esegui questo foglio di stile (idealmente con Saxon di cui IMHO ottiene i rientri di riga a destra) su file più lunghi per ottenere una bella stampa. Per tutti gli elementi in cui si desidera mantenere lo spazio bianco, aggiungere i loro nomi accanto a 'programlisting' come in 'programlisting yourElementName'

HTH


2

Ho preso la versione di Jason Viers e ho aggiunto la logica per inserire le dichiarazioni xmlns sulle loro linee. Questo presume che tu abbia xmlns = e xmlns: senza spazi bianchi intermedi.

(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 sembra una buona modalità. Devo guardarlo. Lo userò se avrò davvero bisogno di tutte le funzionalità che offre.

Ad ogni modo, questo problema mi ha tormentato per circa una settimana e non stavo cercando correttamente. Dopo aver pubblicato, ho iniziato a cercare e ho trovato un sito con una funzione elisp che lo fa abbastanza bene. L'autore suggerisce anche di utilizzare Tidy.

Grazie per la risposta Marcel (peccato non ho abbastanza punti per migliorarti) .

Ne posterò presto sul mio blog. Ecco un post a riguardo (con un collegamento al sito di Marcel).


1

Uso xml-reformat-tagsda xml-parse.el . Di solito vorrai avere il punto all'inizio del file quando esegui questo comando.

È interessante che il file sia incorporato in Emacspeak . Quando stavo usando Emacspeak giorno per giorno, pensavo xml-reformat-tagsfosse un Emacs integrato. Un giorno l'ho perso e ho dovuto fare una ricerca su Internet, quindi sono entrato nella pagina wiki sopra menzionata.

Allego anche il mio codice per avviare xml-parse. Non sono sicuro che questo sia il miglior pezzo di codice Emacs, ma sembra funzionare per me.

(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

Se usi spacemacs , usa semplicemente il comando 'spacemacs / indent-region-or-buffer'.

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

1

dal 2017 emacs è già dotato di questa funzionalità di default, ma devi scrivere questa piccola funzione nel tuo ~/.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))))

quindi chiama M-x reformat-xml

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


0

Temo che la versione di Benjamin Ferrari mi piaccia molto di più. La bella stampa interna posiziona sempre il tag di fine in una nuova riga dopo il valore, inserendo CR indesiderato nei valori del tag.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.