Since no one here has come up with an answer that is sufficient for my needs, I've continued with my aforementioned implementation which uses paredit-backward-delete
under certain conditions. What I've come up with isn't very pretty (nor very efficient), but it works well enough and seems to keep true to the spirit of paredit
from an end-user perspective. Here is the actual function itself, which I named paredit-backward-delete-line
:
(defun paredit-backward-delete-line ()
"Delete line backwards, preserving delimiters and not adding to the kill ring."
(interactive)
;; These variables are set upon invocation. The first one indicates whether this command
;; was invoked inside of a string and the second one is a placeholder variable that will
;; be used later.
(setq-local paredit--started-in-string-p (paredit-in-string-p))
(setq-local paredit--backward-region-p nil)
;; If a sexp or a string is behind us, and
;; * it's not an escaped character
;; * it's not a comment
;; * we're not in a string
;; ...
(if (or (and (not (paredit-in-char-p (1- (point))))
(not (paredit-in-comment-p))
(eq (char-syntax (char-before)) ?\) ))
(and (not (paredit-in-string-p))
(eq (char-syntax (char-before)) ?\" )))
(progn
;; Select the region...
(set-mark-command nil)
(setq deactivate-mark nil)
;; Do a `paredit-backward'...
(paredit-backward)
;; Save the region as a string and indicate that this block of code has been visited.
(setq-local paredit--backward-region (buffer-substring (region-beginning) (region-end)))
(setq-local paredit--backward-region-p t)
;; Then delete the selected region.
(delete-active-region)))
;; If the previous `progn' block has not been run or the region that was selected is only one line...
(if (or (null paredit--backward-region-p) (<= (s-count-matches "\n" paredit--backward-region) 1))
;; Do the following in a loop for the number of the current column...
(dotimes (i (current-column))
;; If a sexp is (still) behind us, recur (this will cause the previous `progn' block to run
;; and get rid of that sexp).
(if (and (not (paredit-in-char-p (1- (point))))
(not (paredit-in-comment-p))
(eq (char-syntax (char-before)) ?\) ))
(paredit-backward-delete-line))
;; Otherwise, make sure: we're not in a comment, that the thing behind us isn't an escaped character,
;; that it's the beginning of a form or list, and that we're in an empty form / list. If that check does
;; not pass, see whether we started in a string and if we're in an empty string currently. If either of
;; these checks pass, do nothing.
(unless (or (and (not (paredit-in-char-p (1- (point))))
(not (paredit-in-comment-p))
(eq (char-syntax (char-before)) ?\( )
(eq (char-after) (matching-paren (char-before))))
(and paredit--started-in-string-p
(eq (1- (point)) (car (paredit-string-start+end-points)))
(eq (point) (cdr (paredit-string-start+end-points)))))
;; If neither of those checks pass, delete backwards when we're in a comment or do a `paredit-backward-delete'
;; otherwise.
(if (paredit-in-comment-p) (delete-backward-char 1) (paredit-backward-delete 1)))))
;; Finally, clear the echo area (`paredit-backward-delete' can be noisy).
(message nil))
Note that it does not kill the line backwards, but rather deletes it. This is actually the behavior that I really wanted in the first place, but I asked the question with killing in mind because I thought it would be easier for someone to implement that because paredit-kill
already exists and does this sort of thing.
Anyway, I bound this function to <C-backspace>
like so:
(define-key paredit-mode-map (kbd "<C-backspace>") 'paredit-backward-delete-line)
And now I enjoy backwards deletion of lines which respects delimiters. Feel free to convert this function to utilize killing instead; it would be fairly easy to do.