Generalize Snarf Tool: How The General Can Be Simpler Than The Specific
1 Executive Summary
The previous article detailed the implementation of a simple function that lets you snarf the contents within a pair of delimiters. That version handled a set of generic delimiters, and errored out if point was not on one of the pre-defined delimiters.
This article shows how that solution can be generalized to cases where point is not on a pre-defined delimiter; in the process, it weighs the pros and cons of usability vs over-generality and shows an implementation that attempts to strike a good balance.
2 The Updated Implementation
(defun snarf-sexp (&optional delete) "Snarf the contents between delimiters at point. Optional interactive prefix arg deletes it." (interactive "P") (let ((orig (point)) (pair nil) (pairs ;;; We keep predefined pairs for usability: '((?< ?>) (?\[ ?\]) (?\( ?\)) (?{ ?}) (?\" ?\") (?' ?') (?` ?') (?| ?|) (?* ?*) (?/ ?/) (?- ?-) (?_ ?_) (?~ ?~))) (char (char-after)) (stab nil)) (setq pair ;;; But we read a close delimiter for the general case (or (assq char pairs) ;;; Predefined delimiter (list char (read-char "Close Delimiter: ")))) ;;; Generality! (setq stab (copy-syntax-table)) (with-syntax-table stab (cond ((= (cl-first pair) (cl-second pair)) (modify-syntax-entry (cl-first pair) "\"" ) (modify-syntax-entry (cl-second pair) "\"" )) (t (modify-syntax-entry (cl-first pair) "(") (modify-syntax-entry (cl-second pair) ")"))) (save-excursion (forward-sexp) (cond (delete (kill-region (1+ orig) (1- (point)))) (t (kill-ring-save (1+ orig) (1- (point)))))))))
3 Key Takeaways
- The generalized implementation no longer throws an error if point is not on a pre-defined delimiter.
- Instead, it generalizes the implementation to read the close delimiter from the keyboard if char at point is not in the pre-defined list.
- We could generalize further by entirely dropping the pre-defined delimiters, but that would hurt usability in the common case where the user would always have to specify the close delimiter.
- Note that usability here is not merely to reduce a keystroke; it's more to reduce the cognitive load on the user with respect to having to think about the close delimiter in the common case.