วิธีจัดการข้อผิดพลาดอย่างนุ่มนวลในไฟล์ init


20

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

มีวิธีใดที่จะทำให้กระบวนการเริ่มต้นเสร็จสิ้นอย่างสง่างามเมื่อเกิดข้อผิดพลาดหรือไม่?

คำตอบ:


9

สองทางเลือกไม่ว่าจะสมบูรณ์แบบ ครั้งแรกที่คุณสามารถตัดส่วนใหญ่ของรหัสการเริ่มต้นของคุณก่อน (กล่าวคือก่อนที่จะได้รับการปรับแต่งของคุณ) (ignore-errors ...)ใน หากมีจะมีข้อผิดพลาด แต่จะไม่มีความคิดเห็นจำนวนมาก - ก็จะกลับมาignore-errorsnil

ตัวเลือกที่สลับซับซ้อนมากขึ้นคือการรวมโค้ด buggy ไว้ในชุดค่าผสมunwind-protectและwith-demoted-errors(พร้อมdebug-on-errorตั้งค่าเป็นศูนย์) หลังจะเกิดข้อผิดพลาดอย่างสง่างาม - ish ที่ข้อผิดพลาดแรกที่พบและรายงานข้อผิดพลาดไปยัง*Messages*บัฟเฟอร์สำหรับการตรวจสอบของคุณ ในขณะเดียวกันส่วนที่เหลือของunwind-protectร่างกาย (สมมุติว่าการปรับแต่งของคุณ) จะได้รับการประเมิน ดังนั้นเช่น:

(unwind-protect
    (let ((debug-on-error nil))
      (with-demoted-errors
        (message "The world is about to end")
        (sleep-for 2)
        (/ 10 0)                        ; divide by zero signals an error
        (message "This will never evaluate")
        (sleep-for 2)
        (setq some-var 5)))
  (message "Here are the unwind forms that will always evaluate")
  (sleep-for 2)
  (setq some-var 10)
  (setq another-var "puppies")
  (message "All done!"))

1
with-demoted-errorsดีฉันไม่ได้เกี่ยวกับ คุณสามารถเพิ่มอากิวเมนต์ของสตริงได้"LOOK OVER HERE!!! %s"ดังนั้นคุณมีโอกาสน้อยที่จะพลาดข้อผิดพลาดในบัฟเฟอร์ข้อความ
Malabarba

@Malabarba แบบฟอร์มwith-demoted-errorsนี้มีให้บริการเฉพาะวันที่ 24.4
จันทร์ที่

@ Lunaryorn ขอบคุณไม่ทราบว่า
Malabarba

ที่จริงแล้วเวอร์ชันที่ฉันใช้อยู่คือ 24.3.1
แดน

8

@Dan อธิบายได้ดีว่าคุณจะเปลี่ยนข้อผิดพลาดเป็นข้อความได้อย่างไร condition-caseนอกจากนี้คุณยังสามารถทำสิ่งที่คุณต้องการมีข้อผิดพลาดโดยใช้ unwind-protectแต่อีกทางเลือกหนึ่งคือการใช้งาน

ฉันจะอยู่condition-caseที่นี่ไม่ว่าด้วยเหตุผลใดก็ตาม

การตรวจจับข้อผิดพลาด

condition-caseนี้ควรรับประกันคำจำกัดความที่สำคัญของคุณได้รับการประเมินโดยไม่คำนึงถึงสิ่งที่เกิดขึ้นภายใน ข้อผิดพลาดใด ๆ init-errorที่ได้รับการเก็บไว้ภายใน

(defvar init-error nil 
  "The error which happened.")

(condition-case the-error
    (progn
      ;; Do the dangerous stuff here.
      (require 'what-I-want))
  (error
   ;; This is only evaluated if there's an error.
   (setq init-error the-error)))

;;; Do the safe stuff here.
(define-key uncrippling-map "\C-h" 'help!)

ขว้างมันกลับ

หลังจากนั้นให้โยนข้อผิดพลาดอีกครั้ง มีหลายวิธีที่คุณสามารถทำได้เช่นนี้

;;; Throw the error again here.
(when init-error
  (funcall #'signal (car init-error) (cdr init-error)))

unwind-protectทำให้เกิดข้อผิดพลาดขึ้นอีกครั้งทันทีหลังจากรันโค้ดอะไรก็ตามที่คุณใส่ไว้ในประโยคช่วยเหลือ มันเหมือนfinallyในภาษาเช่น Java catchมากกว่า
sanityinc

2

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

(defun my/require-softly (feature &optional filename)
  "As `require', but instead of an error just print a message.

If there is an error, its message will be included in the message
printed.

Like `require', the return value will be FEATURE if the load was
successful (or unnecessary) and nil if not."
  (condition-case err
      (require feature filename) 
    (error (message "Error loading %s: \"%s\""
                    (if filename (format "%s (%s)" feature filename) feature)
                    (error-message-string err))
           nil)))

ข้อผิดพลาดขณะโหลดไฟล์ด้วยวิธีนี้จะยังคงพิมพ์ข้อความ แต่จะไม่ป้องกันการดำเนินการใด ๆ นอกไฟล์ที่เกิดข้อผิดพลาดจริง

แน่นอนว่าฟังก์ชั่นนี้ไม่ได้แตกต่างจากการวางrequireสายwith-demoted-errors(ฉันเขียนก่อนที่ฉันจะรู้with-demoted-errors) แต่ประเด็นสำคัญก็คือคุณสามารถนำบางสิ่งอย่างเช่นการรวมกันของแดนเข้าด้วยกันwith-demoted-errorsและunwind-protectไม่ห่อหุ้ม (อาจยาวมาก) บล็อกของรหัส


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