Hübsches Drucken von XML-Dateien auf Emacs

83

Ich benutze Emacs, um meine XML-Dateien zu bearbeiten (nxml-Modus) und die Dateien, die vom Computer generiert wurden, haben keine hübsche Formatierung der Tags.

Ich habe nach hübschem Drucken der gesamten Datei mit Einrückung und Speichern gesucht, konnte aber keinen automatischen Weg finden.

Gibt es einen Weg? Oder zumindest einen Editor unter Linux, der das kann.

cnu
quelle

Antworten:

25

Ich verwende den nXML-Modus zum Bearbeiten und Aufräumen, wenn ich XML oder HTML formatieren und einrücken möchte. Es gibt auch eine Emacs-Schnittstelle zu Tidy.

Marcel Levy
quelle
Bis Ende 2013 läuft tidy.el Version: 20111222.1756 nicht auf Emacs 24 mitwrong type argument: stringp, nil
keiw
@keiw Das liegt wahrscheinlich daran, dass Sie es in einem Puffer tun, der keinen Dateinamen hat. Habe den gleichen Fehler und habe ihn zumindest auf meiner Seite darauf zurückgeführt.
Alf
108

Sie müssen nicht einmal Ihre eigene Funktion schreiben - der sgml-Modus (ein Gnu-Emacs-Kernmodul) verfügt über eine integrierte hübsche Druckfunktion namens (sgml-Pretty-Print ...), die Anfangs- und Endargumente für Regionen verwendet.

Wenn Sie XML ausschneiden und einfügen und feststellen, dass Ihr Terminal die Zeilen an beliebigen Stellen zerhackt, können Sie diesen hübschen Drucker verwenden, der zuerst unterbrochene Zeilen behebt.

Juan Garcia
quelle
1
(sgml-hübsch-drucken (Region-Anfang) (Region-Ende))
ScootyPuff
6
Ich bin mir nicht sicher, wie sgml-modesich das im Laufe der Zeit verändert haben könnte. Heute habe ich angerufen C-x C-f foo.xml, M-x sgml-modeund dann M-x sgml-pretty-printund meine XML - Datei wurde ziemlich gedruckt. (Nun, Emacs hingen zwanzig Sekunden oder länger, bevor sie
fertig waren
1
Eigentlich musste ich auch C-x gden gesamten Puffer als Region auswählen.
Daveloyall
3
Ich musste nicht einmal in den SGML-Modus wechseln. Es war ein Mx-Befehl im nXML-Modus!
Nroose
1
Mit Emacs 26.2 kann ich im nXML-Modus bleiben, den gesamten Puffer auswählen C-x hund dann M-x sgml-pretty-print. Die XML wird jetzt ziemlich formatiert sein
Swedgin
86

Wenn Sie nur einen hübschen Einzug benötigen, ohne neue Zeilenumbrüche einzuführen, können Sie den indent-regionBefehl mit den folgenden Tastenanschlägen auf den gesamten Puffer anwenden :

C-x h
C-M-\

Wenn Sie auch Zeilenumbrüche einführen müssen, damit sich das Öffnen und Schließen von Tags in getrennten Zeilen befindet, können Sie die folgende sehr schöne Elisp-Funktion verwenden, die von Benjamin Ferrari geschrieben wurde . Ich habe es auf seinem Blog gefunden und hoffe, dass ich es hier reproduzieren kann:

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

Dies hängt nicht von einem externen Tool wie Tidy ab.

Christian Berg
quelle
1
Gut, danke. Durch Entfernen des (nxml-Modus) aus dem obigen Defun-Print-Defun kann er im in Emacs 22.2.1 integrierten SGML-Modus arbeiten. Aber ich habe es modifiziert, um den gesamten Puffer (Punkt-Min) auf (Punkt-Max) zu setzen, weil das meine Hauptsache ist. Außerdem ein Fehler: Für jede neue Zeile, die Sie einfügen, müssen Sie das Ende erhöhen.
Cheeso
Wie kann ich diese Funktion in Emacs verwenden? Ich habe den Funktionscode in den Arbeitspuffer kopiert, eingefügt und ausgewertet. Wie rufe ich diese Funktion auf?
Alexandre Rademaker
1
Nachdem Sie den Defun ausgewertet haben, können Sie ihn wie jede andere Funktion aufrufen: Mx bf-pretty-print-xml-region. (Sie müssen natürlich nicht alles eingeben, verwenden Sie die Tab-Vervollständigung: Mx bf <tab> sollte ausreichen.) Sie möchten die Funktion wahrscheinlich nicht jedes Mal definieren, wenn Sie sie verwenden möchten, also platzieren Sie sie irgendwo wo es zur Startzeit geladen wird, zB in ~ / .emacs.d / init.el
Christian Berg
1
Wie wäre es mit langen Attributlisten?
Ceving
Das ist fabelhaft, denn ordentlich beschwert sich über ungültige Zeichenkodierungen und möchte, dass ich sie bereinige, bevor die Datei neu formatiert wird! Manchmal geht es darum, die Struktur einer kaputten XML-Datei zu sehen, und ordentlich weigert sich zu helfen.
TauPan
35

Emacs können beliebige Befehle mit M- | ausführen. Wenn Sie xmllint installiert haben:

"M- | xmllint --format -" formatiert die ausgewählte Region

"Cu M- | xmllint --format -" macht dasselbe und ersetzt den Bereich durch die Ausgabe

Tim Helmstedt
quelle
Verwenden Sie Mx Mark-Whole-Buffer vor, um den gesamten Pufferinhalt als zu verarbeitenden Bereich zu markieren.
Harald
19

Dank Tim Helmstedt oben habe ich so gemacht:

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

schnell und einfach. Danke vielmals.

Bubak
quelle
2
Dies gab mir einen Fehler auf GNU Emacs 24, so dass ich die letzte Zeile geändert habe zu:(indent-region 0 (count-lines (point-min) (point-max)))
John J. Camilleri
19

Zum Einführen von Zeilenumbrüchen und dann zum hübschen Drucken

M-x sgml-mode
M-x sgml-pretty-print
Talespin_Kit
quelle
8

Hier sind einige Verbesserungen, die ich an Benjamin Ferraris Version vorgenommen habe:

  • Das search-forward-regexphat kein Ende angegeben, also würde es mit Sachen vom Anfang der Region bis zum Ende des Puffers (anstelle des Endes der Region) arbeiten.
  • Inkrementiert jetzt endrichtig, wie Cheeso bemerkte.
  • Es würde eine Unterbrechung zwischen einfügen <tag></tag>, wodurch der Wert geändert wird. Ja, technisch gesehen ändern wir hier die Werte von allem, aber ein leerer Start / Ende ist viel wahrscheinlicher von Bedeutung. Verwendet jetzt zwei separate, etwas strengere Suchvorgänge, um dies zu vermeiden.

Hat noch das "verlässt sich nicht auf externe Ordnung" usw. Allerdings erfordert es clfür das incfMakro.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 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
quelle
5

Eine Möglichkeit ist, wenn Sie etwas im folgenden Format haben

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

Versuchen Sie es in Emacs

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

Dies wird über dem XML-Beispiel nach unten eingerückt

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

In VIM können Sie dies tun, indem Sie

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

Hoffe das hilft.

user1028948
quelle
2
  1. Der Emacs nxml-Modus kann mit dem präsentierten Format arbeiten, aber Sie müssen die Zeilen teilen.
  2. Für längere Dateien lohnt sich das einfach nicht. Führen Sie dieses Stylesheet (idealerweise mit Saxon, bei dem IMHO die Zeileneinrückungen ungefähr richtig sind) gegen längere Dateien aus, um einen schönen, hübschen Druck zu erhalten. Fügen Sie für alle Elemente, bei denen Sie Leerzeichen beibehalten möchten, deren Namen neben "Programmliste" hinzu, wie unter "Programmliste Ihres Elementnamens".

HTH

DaveP
quelle
2

Ich nahm die Version von Jason Viers und fügte Logik hinzu, um XML-Deklarationen in ihre eigenen Zeilen zu setzen. Dies setzt voraus, dass Sie xmlns = und xmlns haben: ohne dazwischenliegende Leerzeichen.

(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
quelle
1

Ordentlich sieht aus wie ein guter Modus. Muss es anschauen. Ich werde es verwenden, wenn ich wirklich alle Funktionen brauche, die es bietet.

Wie auch immer, dieses Problem quälte mich ungefähr eine Woche lang und ich suchte nicht richtig. Nach dem Posten habe ich angefangen zu suchen und eine Seite mit einer Elisp-Funktion gefunden, die es ziemlich gut macht. Der Autor schlägt auch vor, Tidy zu verwenden.

Danke für die Antwort Marcel (schade, dass ich nicht genug Punkte habe, um dich zu verbessern) .

Werde bald darüber auf meinem Blog posten. Hier ist ein Beitrag darüber (mit einem Link zu Marcel's Seite).

cnu
quelle
1

Ich benutze xml-reformat-tagsvon xml-parse.el . Normalerweise möchten Sie den Punkt am Anfang der Datei haben, wenn Sie diesen Befehl ausführen.

Es ist interessant, dass die Datei in Emacspeak integriert ist . Als ich Emacspeak Tag für Tag benutzte, dachte ich, es xml-reformat-tagssei ein Emacs eingebaut. Eines Tages verlor ich es und musste danach im Internet suchen und betrat damit die oben erwähnte Wiki-Seite.

Ich füge auch meinen Code hinzu, um xml-parse zu starten. Ich bin mir nicht sicher, ob dies der beste Teil des Emacs-Codes ist, scheint aber für mich zu funktionieren.

(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
quelle
1

Wenn Sie Spacemacs verwenden , verwenden Sie einfach den Befehl 'spacemacs / indent-region-or-buffer'.

M-x spacemacs/indent-region-or-buffer
JohnnyZ
quelle
1

Emacs ist ab 2017 standardmäßig bereits mit dieser Funktion ausgestattet, aber Sie müssen diese kleine Funktion in Ihre ~/.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))))

dann ruf einfach an M-x reformat-xml

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

Ninrod
quelle
0

Ich fürchte, ich mag die Benjamin Ferrari-Version viel besser. Der interne hübsche Druck platziert das End-Tag immer in einer neuen Zeile nach dem Wert und fügt unerwünschte CR in die Tag-Werte ein.


quelle