Ładne drukowanie plików XML na Emacsie

84

Używam emacsa do edycji moich plików xml (tryb nxml), a pliki wygenerowane przez maszynę nie mają ładnego formatowania tagów.

Szukałem ładnego drukowania całego pliku z wcięciem i zapisywania go, ale nie byłem w stanie znaleźć automatycznego sposobu.

Czy istnieje sposób? Lub przynajmniej jakiś edytor na Linuksie, który to potrafi.

cnu
źródło

Odpowiedzi:

25

Używam trybu nxml do edycji i Tidy , kiedy chcę i tiret formacie XML lub HTML. Istnieje również interfejs Emacsa do Tidy.

Marcel Levy
źródło
Do końca 2013 r. Tidy.el Wersja: 20111222.1756 nie działa na Emacs 24 zwrong type argument: stringp, nil
keiw
@keiw To prawdopodobnie dlatego, że robisz to w buforze, który nie ma nazwy pliku. Mam ten sam błąd i wyśledziłem to przynajmniej po mojej stronie.
Alf
108

Nie musisz nawet pisać własnej funkcji - sgml-mode (podstawowy moduł emacsa GNU) ma wbudowaną ładną funkcję drukującą o nazwie (sgml-pretty-print ...), która pobiera argumenty początku i końca regionu.

Jeśli wycinasz i wklejasz xml i zauważysz, że twój terminal obcina linie w dowolnych miejscach, możesz użyć tej ładnej drukarki, która najpierw naprawia przerywane linie.

Juan Garcia
źródło
1
(sgml-pretty-print (region-
start
7
Nie jestem pewien, jak sgml-modemogło się to zmienić w czasie. Dzisiaj wywołałem C-x C-f foo.xml, M-x sgml-modei wtedy M-x sgml-pretty-printmój plik xml został całkiem wydrukowany. (Cóż, emacs wisiał na dwadzieścia sekund lub dłużej przed ukończeniem. Był to plik z jedną linijką przed ładnym wydrukiem i 720 wierszami po.)
daveloyall
1
Właściwie musiałem też zrobić, C-x gaby wybrać cały bufor jako region.
daveloyall
3
Nie musiałem nawet przełączać się na tryb sgml. To była komenda Mx w trybie nXML!
nroose
1
Używając Emacsa 26.2 mogę pozostać w trybie nXML, wybrać cały bufor C-x hi wtedy M-x sgml-pretty-print. XML będzie teraz dość sformatowany
Swedgin
87

Jeśli potrzebujesz tylko ładnych wcięć bez wprowadzania nowych podziałów linii, możesz zastosować indent-regionpolecenie do całego bufora za pomocą tych naciśnięć klawiszy:

C-x h
C-M-\

Jeśli chcesz również wprowadzić podziały wierszy, aby znaczniki otwierające i zamykające znajdowały się w osobnych wierszach, możesz użyć następującej bardzo ładnej funkcji elisp, napisanej przez Benjamina Ferrariego . Znalazłem go na jego blogu i mam nadzieję, że mogę go tutaj odtworzyć:

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

Nie polega to na zewnętrznym narzędziu, takim jak Tidy.

Christian Berg
źródło
1
Dobrze, dzięki. Usunięcie (nxml-mode) z powyższego pretty-print defun pozwala mu działać w trybie sgml, który jest wbudowany w emacs 22.2.1. Ale zmodyfikowałem to, aby zrobić cały bufor (punkt-min) do (punkt-max), ponieważ to moja główna rzecz. Poza tym jeden błąd: dla każdego wstawionego nowego wiersza będziesz musiał zwiększyć koniec.
Cheeso
Jak mogę używać tej funkcji w Emacsie? Skopiowałem i wkleiłem kod funkcji w bufora magazynującego i oceniłem go. Jak teraz wywołać tę funkcję?
Alexandre Rademaker,
1
Po oszacowaniu defun, możesz wywołać ją jak każdą inną funkcję: Mx bf-pretty-print-xml-region. (Nie musisz wpisywać wszystkiego, oczywiście, użyj uzupełniania tabulatorami: Mx bf <tab> powinno wystarczyć.) Prawdopodobnie nie chcesz definiować funkcji za każdym razem, gdy chcesz jej użyć, więc umieść ją gdzieś gdzie jest ładowany przy starcie, np. w ~ / .emacs.d / init.el
Christian Berg
1
A co z łamaniem długich list atrybutów?
ceving
To wspaniałe, ponieważ porządek narzeka na nieprawidłowe kodowanie znaków i chce, żebym je wyczyścił, zanim sformatuje plik! Czasami chodzi o to, aby zobaczyć strukturę zepsutego pliku xml, a porządek odmówi pomocy.
TauPan
35

Emacs może uruchamiać dowolne polecenia za pomocą M- |. Jeśli masz zainstalowany xmllint:

„M- | xmllint --format -” sformatuje wybrany region

"Cu M- | xmllint --format -" zrobi to samo, zastępując region wyjściem

Tim Helmstedt
źródło
Użyj Mx mark-whole-buffer z przodu, aby oznaczyć całą zawartość buforu jako region do przetworzenia.
Harald
19

Dzięki Timowi Helmstedtowi powyżej zrobiłem tak:

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

szybko i łatwo. Wielkie dzięki.

bubak
źródło
2
To dało mi błąd w GNU Emacs 24, więc zmieniłem ostatnią linię na:(indent-region 0 (count-lines (point-min) (point-max)))
John J. Camilleri
19

Do wprowadzania podziałów wiersza, a następnie ładnego drukowania

M-x sgml-mode
M-x sgml-pretty-print
Talespin_Kit
źródło
8

oto kilka poprawek, które wprowadziłem do wersji Benjamina Ferrariego:

  • search-forward-regexpnie określił kres, tak by działać na rzeczy od początku do końca z regionu buforu (zamiast końca regionu)
  • Teraz endrośnie prawidłowo, jak zauważył Cheeso.
  • wstawiłby przerwę między <tag></tag>, co modyfikuje jego wartość. Tak, technicznie rzecz biorąc, modyfikujemy tutaj wartości wszystkiego, ale pusty początek / koniec jest znacznie bardziej istotny. Teraz używa dwóch oddzielnych, nieco bardziej ścisłych wyszukiwań, aby tego uniknąć.

Nadal ma „nie polega na zewnętrznym porządku” itp. Jednak wymaga cltego incfmakra.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 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
źródło
5

Jednym ze sposobów jest, jeśli masz coś w poniższym formacie

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

W Emacsie spróbuj

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

Spowoduje to wcięcie powyższego przykładu XML do poniższego

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

W VIM możesz to zrobić przez

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

Mam nadzieję że to pomoże.

user1028948
źródło
2
  1. Emacs nxml-mode może pracować na prezentowanym formacie, ale będziesz musiał podzielić linie.
  2. W przypadku dłuższych plików, które po prostu nie są tego warte. Uruchom ten arkusz stylów (najlepiej z Saxonem, w którym IMHO ustawia wcięcia linii) na dłuższych plikach, aby uzyskać ładny, ładny wydruk. Dla każdego elementu, w którym chcesz zachować odstępy, dodaj ich nazwy obok „programlisting”, jak w „programlisting yourElementName”

HTH

DaveP
źródło
2

Wziąłem wersję Jason Viers' i dodał logiki umieścić deklaracji xmlns na swoich liniach. Zakłada się, że masz xmlns = i xmlns: bez pośrednich spacji.

(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!"))
Ser
źródło
1

Tidy wygląda na dobry tryb. Muszę na to spojrzeć. Użyję go, jeśli naprawdę będę potrzebować wszystkich funkcji, które oferuje.

Zresztą ten problem dokuczał mi przez około tydzień i nie szukałem poprawnie. Po wysłaniu zacząłem szukać i znalazłem jedną witrynę z funkcją elisp, która robi to całkiem nieźle. Autor sugeruje również użycie Tidy.

Dzięki za odpowiedź Marcel (szkoda, że ​​nie mam wystarczającej liczby punktów, aby cię ulepszyć) .

Wkrótce opublikuję o tym na moim blogu. Oto post na ten temat (z linkiem do strony Marcela).

cnu
źródło
1

Używam xml-reformat-tagsz xml-parse.el . Zwykle będziesz chciał mieć punkt na początku pliku podczas uruchamiania tego polecenia.

Ciekawe, że plik jest włączony do Emacspeak . Kiedy używałem Emacspeak na co dzień, myślałem, że xml-reformat-tagsto wbudowany Emacs. Pewnego dnia go zgubiłem i musiałem poszukać tego w Internecie, a tym samym wszedłem na wspomnianą powyżej stronę wiki.

Załączam również mój kod, aby rozpocząć analizę xml. Nie jestem pewien, czy to najlepszy fragment kodu Emacsa, ale wydaje mi się, że działa dla mnie.

(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
źródło
1

Jeśli używasz spacemacs , po prostu użyj polecenia „spacemacs / indent-region-or-buffer”.

M-x spacemacs/indent-region-or-buffer
JohnnyZ
źródło
1

od 2017 roku emacs ma już tę możliwość domyślnie, ale musisz zapisać tę małą funkcję w swoim ~/.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))))

po prostu zadzwoń M-x reformat-xml

źródło: https://davidcapello.com/blog/emacs/reformat-xml-on-emacs/

ninrod
źródło
0

Obawiam się, że wersja Benjamina Ferrari bardziej mi się podoba. Wewnętrzny ładny wydruk zawsze umieszcza znacznik końcowy w nowym wierszu po wartości, wstawiając niechciane CR w wartościach znaczników.


źródło