พิมพ์ไฟล์ XML สวย ๆ บน Emacs


84

ฉันใช้ emac เพื่อแก้ไขไฟล์ xml ของฉัน (โหมด nxml) และไฟล์ที่สร้างขึ้นโดยเครื่องไม่มีการจัดรูปแบบแท็กที่สวยงาม

ฉันค้นหาการพิมพ์สวย ๆ ทั้งไฟล์ด้วยการเยื้องและบันทึก แต่ไม่พบวิธีอัตโนมัติ

มีวิธีไหม? หรืออย่างน้อยตัวแก้ไขบน linux ซึ่งสามารถทำได้

คำตอบ:


25

ฉันใช้โหมด nXMLเพื่อแก้ไขและจัดระเบียบเมื่อฉันต้องการจัดรูปแบบและเยื้อง XML หรือ HTML นอกจากนี้ยังมีอินเทอร์เฟซ Emacs เพื่อ Tidy


ภายในสิ้นปี 2013 tidy.el เวอร์ชัน: 20111222.1756 ล้มเหลวในการทำงานบน Emacs 24 ด้วยwrong type argument: stringp, nil
keiw

@keiw นั่นอาจเป็นเพราะคุณใช้บัฟเฟอร์ที่ไม่มีชื่อไฟล์ มีข้อผิดพลาดเดียวกันและตรวจสอบว่าอย่างน้อยก็ในด้านของฉัน
Alf

110

คุณไม่จำเป็นต้องเขียนฟังก์ชันของคุณเองด้วยซ้ำ - โหมด sgml (โมดูลแกน gnu emacs) มีฟังก์ชันการพิมพ์ที่สวยงามในตัวที่เรียกว่า (sgml-pretty-print ... ) ซึ่งใช้อาร์กิวเมนต์เริ่มต้นและสิ้นสุดของภูมิภาค

หากคุณกำลังตัดและวาง xml และคุณพบว่าเทอร์มินัลของคุณกำลังตัดเส้นในสถานที่ใด ๆ คุณสามารถใช้เครื่องพิมพ์ที่สวยงามนี้ซึ่งจะแก้ไขเส้นที่ขาดก่อน


1
(sgml-pretty-print (region-
start

7
ฉันไม่แน่ใจว่าsgml-modeจะมีการเปลี่ยนแปลงอย่างไรบ้างเมื่อเวลาผ่านไป วันนี้ผมเรียกC-x C-f foo.xml, M-x sgml-modeแล้วM-x sgml-pretty-printและไฟล์ xml ของฉันได้พิมพ์สวย (อืมอีแมคถูกแขวนคอเป็นเวลายี่สิบวินาทีขึ้นไปก่อนจะเสร็จมันเป็นไฟล์หนึ่งบรรทัดก่อนพิมพ์สวยและ 720 บรรทัดหลังจากนั้น)
daveloyall

1
ที่จริงฉันต้องC-x gเลือกบัฟเฟอร์ทั้งหมดเป็นภูมิภาคด้วย
daveloyall

3
ฉันไม่จำเป็นต้องเปลี่ยนไปใช้โหมด sgml มันเป็นคำสั่ง Mx ในโหมด nXML!
จมูก

1
ใช้ Emacs 26.2 ผมสามารถอยู่ในโหมด nXML เลือกบัฟเฟอร์ทั้งหมดแล้วC-x h M-x sgml-pretty-printxml จะมีรูปแบบที่สวยงามในขณะนี้
Swedgin

87

หากคุณต้องการเพียงแค่การเยื้องโดยไม่ต้องขึ้นบรรทัดใหม่คุณสามารถใช้indent-regionคำสั่งกับบัฟเฟอร์ทั้งหมดด้วยการกดแป้นเหล่านี้:

C-x h
C-M-\

หากคุณยังจำเป็นต้องแนะนำสายแบ่งเพื่อให้เปิดและปิดแท็กอยู่บนเส้นที่แยกจากกันคุณสามารถใช้ฟังก์ชั่น Elisp ต่อไปนี้ดีมากเขียนโดยเบนจามินเฟอร์รารี ฉันพบมันในบล็อกของเขาและหวังว่าฉันจะทำซ้ำได้ที่นี่:

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

สิ่งนี้ไม่ได้ขึ้นอยู่กับเครื่องมือภายนอกเช่น Tidy


1
ขอบคุณมาก การลบ (โหมด nxml) ออกจากโหมดการพิมพ์สวยด้านบนช่วยให้สามารถทำงานในโหมด sgml ที่ติดตั้งใน emacs 22.2.1 ได้ แต่ฉันแก้ไขให้ทำบัฟเฟอร์ทั้งหมด (point-min) เป็น (point-max) เพราะนั่นคือสิ่งสำคัญของฉัน นอกจากนี้ข้อบกพร่องประการหนึ่ง: สำหรับการขึ้นบรรทัดใหม่แต่ละครั้งที่คุณแทรกคุณจะต้องเพิ่มจุดสิ้นสุด
Cheeso

ฉันจะใช้ฟังก์ชันนี้ใน Emacs ได้อย่างไร? ฉันได้คัดลอกและวางโค้ดฟังก์ชันในบัฟเฟอร์ขูดและประเมินแล้ว ตอนนี้ฉันจะเรียกใช้ฟังก์ชันนี้ได้อย่างไร
Alexandre Rademaker

1
หลังจากประเมินค่า defun แล้วคุณสามารถเรียกใช้งานได้เหมือนกับฟังก์ชันอื่น ๆ : Mx bf-pretty-print-xml-region (คุณไม่จำเป็นต้องพิมพ์ทั้งหมดแน่นอนใช้ tab complete: Mx bf <tab> ก็น่าจะเพียงพอแล้ว) คุณอาจไม่ต้องการกำหนดฟังก์ชันทุกครั้งที่ต้องการใช้ดังนั้นให้วางไว้ที่ใดก็ได้ ที่โหลดในเวลาเริ่มต้นเช่นใน ~ / .emacs.d / init.el
Christian Berg

1
วิธีการทำลายรายการแอตทริบิวต์ยาว?
สิ้นสุด

นี่เป็นเรื่องเยี่ยมเพราะมีการบ่นเกี่ยวกับการเข้ารหัสอักขระที่ไม่ถูกต้องและต้องการให้ฉันทำความสะอาดก่อนที่จะฟอร์แมตไฟล์ใหม่! บางครั้งประเด็นคือการเห็นโครงสร้างของไฟล์ xml ที่เสียและไม่เรียบร้อยจะปฏิเสธที่จะช่วยเหลือ
TauPan

35

Emac สามารถรันคำสั่งตามอำเภอใจด้วย M- | หากคุณติดตั้ง xmllint:

"M- | xmllint --format -" จะจัดรูปแบบภูมิภาคที่เลือก

"Cu M- | xmllint --format -" จะทำเช่นเดียวกันโดยแทนที่ภูมิภาคด้วยเอาต์พุต


ใช้เครื่องหมาย Mx-whole-buffer ด้านหน้าเพื่อทำเครื่องหมายเนื้อหาบัฟเฟอร์ทั้งหมดเป็นพื้นที่ที่จะประมวลผล
Harald

19

ขอบคุณ Tim Helmstedt ข้างต้นฉันทำสิ่งนี้:

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

ง่ายและรวดเร็ว ขอบคุณมาก.


2
สิ่งนี้ทำให้ฉันมีข้อผิดพลาดใน GNU Emacs 24 ดังนั้นฉันจึงเปลี่ยนบรรทัดสุดท้ายเป็น:(indent-region 0 (count-lines (point-min) (point-max)))
John J. Camilleri


8

นี่คือการปรับแต่งเล็กน้อยที่ฉันทำกับเวอร์ชันของ Benjamin Ferrari:

  • ที่ search-forward-regexpไม่ได้ระบุสิ้นจึงจะดำเนินการในสิ่งที่จากจุดเริ่มต้นของภูมิภาคที่จะสิ้นสุดของบัฟเฟอร์ (แทนของการสิ้นสุดของภูมิภาค)
  • ตอนนี้เพิ่มขึ้น endอย่างถูกต้องตามที่ Cheeso กล่าวไว้
  • มันจะแทรกตัวแบ่งระหว่าง<tag></tag>ซึ่งปรับเปลี่ยนค่าของมัน ใช่ในทางเทคนิคเรากำลังแก้ไขค่าของทุกอย่างที่นี่ แต่การเริ่มต้น / สิ้นสุดที่ว่างเปล่านั้นมีแนวโน้มที่จะมีความสำคัญมากกว่า ตอนนี้ใช้การค้นหาสองรายการแยกกันและเข้มงวดขึ้นเล็กน้อยเพื่อหลีกเลี่ยงปัญหา

ยังคงมี "ไม่พึ่งพาภายนอกเป็นระเบียบเรียบร้อย" ฯลฯ แต่ก็ไม่จำเป็นต้องมีclสำหรับincfแมโคร

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

วิธีหนึ่งในการทำคือหากคุณมีบางอย่างในรูปแบบด้านล่าง

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

ใน Emacs ลอง

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

สิ่งนี้จะเยื้องตัวอย่าง xml ด้านบนไปด้านล่าง

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

ใน VIM คุณสามารถทำได้โดย

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

หวังว่านี่จะช่วยได้


2
  1. โหมด Emacs nxml สามารถทำงานกับรูปแบบที่นำเสนอได้ แต่คุณจะต้องแบ่งบรรทัด
  2. สำหรับไฟล์ที่ยาวขึ้นซึ่งไม่คุ้มค่า เรียกใช้สไตล์ชีตนี้ (โดยเฉพาะอย่างยิ่งกับ Saxon ซึ่ง IMHO ได้รับการเยื้องบรรทัดด้านขวา) กับไฟล์ที่ยาวขึ้นเพื่อให้ได้งานพิมพ์ที่สวยงาม สำหรับองค์ประกอบใด ๆ ที่คุณต้องการรักษาพื้นที่สีขาวให้เพิ่มชื่อของพวกเขาควบคู่ไปกับ 'programlisting' เช่นเดียวกับ 'programlisting yourElementName'

HTH


2

ฉันใช้เวอร์ชันของ Jason Viersและเพิ่มตรรกะเพื่อใส่การประกาศ xmlns ในบรรทัดของตัวเอง สิ่งนี้จะถือว่าคุณมี xmlns = และ xmlns: โดยไม่มีช่องว่างที่แทรกแซง

(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 ดูเหมือนเป็นโหมดที่ดี ต้องดูเลย. จะใช้ถ้าฉันต้องการคุณสมบัติทั้งหมดที่มีให้จริงๆ

อย่างไรก็ตามปัญหานี้รบกวนฉันมาประมาณหนึ่งสัปดาห์แล้วและฉันก็ค้นหาไม่ถูกต้อง หลังจากโพสต์ฉันเริ่มค้นหาและพบไซต์หนึ่งที่มีฟังก์ชัน elispซึ่งทำได้ดีทีเดียว ผู้เขียนยังแนะนำให้ใช้ Tidy

ขอบคุณสำหรับคำตอบ Marcel (แย่จังฉันมีคะแนนไม่พอที่จะอัปโมดคุณ)คุณ)

จะโพสต์เกี่ยวกับเรื่องนี้ในบล็อกของฉันเร็ว ๆ นี้ นี่คือโพสต์เกี่ยวกับเรื่องนี้ (พร้อมลิงก์ไปยังเว็บไซต์ของ Marcel)


1

ผมใช้xml-reformat-tagsจากXML-parse.el โดยปกติคุณจะต้องมีจุดที่จุดเริ่มต้นของไฟล์เมื่อเรียกใช้คำสั่งนี้

มันเป็นเรื่องที่น่าสนใจที่ไฟล์จะถูกรวมอยู่ในEmacspeak เมื่อฉันใช้ Emacspeak ในแต่ละวันฉันคิดว่าxml-reformat-tagsเป็น Emacs ในตัว วันหนึ่งฉันทำมันหายและต้องทำการค้นหาทางอินเทอร์เน็ตจึงเข้าสู่หน้าวิกิที่กล่าวถึงข้างต้น

ฉันกำลังแนบรหัสของฉันเพื่อเริ่ม xml-parse ไม่แน่ใจว่านี่เป็นโค้ด Emacs ที่ดีที่สุดหรือไม่ แต่ดูเหมือนจะใช้ได้กับฉัน

(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

ในปี 2017 emac มาพร้อมกับความสามารถนี้แล้วโดยค่าเริ่มต้น แต่คุณต้องเขียนฟังก์ชันเล็ก ๆ นี้ลงใน~/.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))))

จากนั้นโทร M-x reformat-xml

แหล่งที่มา: https://davidcapello.com/blog/emacs/reformat-xml-on-emacs/


0

ฉันกลัวว่าฉันชอบเบนจามินเฟอร์รารีเวอร์ชั่นมากกว่า การพิมพ์สวยภายในจะวางแท็กปิดท้ายไว้ในบรรทัดใหม่หลังค่าเสมอโดยแทรก CR ที่ไม่ต้องการในค่าแท็ก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.