5

Most everyone who uses paredit has used paredit-kill (i.e., C-k), which allows one to delete a line forwards whilst keeping delimiters balanced properly. But how does one do this in the reverse direction? When one does C-0 C-k, which is something that's typically used to kill the line backwards, paredit-kill actually calls the built-in kill-line to handle it. Unfortunately, this loses the delimiter-balancing that is otherwise had with paredit-kill. Since discovering that C-0 C-k does not work quite right, I've tried implementing a paredit-backward-kill-line function that will kill backwards and balance delimiters a la paredit-kill. However, I've not had much luck with this and have only come up with an implementation which uses paredit-backward-delete under certain conditions. This solution has only worked marginally well for me thus far. I've also tried reworking some of paredit's functions that relate to killing lines, but it seems that they are all hard-coded to work with forwards deletion. This is making certain functions here significantly difficult to rework.

All in all, this seems like a lot of work to get something working that seems like it should be relatively simple, given that paredit already works as well as it does. So, I guess my question is: is there a way to do a backwards paredit-kill which retains all the delimiter-balancing functionality that is had from a normal, forwards paredit-kill?

1

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.

0

You could use lispy. What you describe can be achieved by marking a sexp (m or M-m), switching to the different side of the region (d), and growing the region by as many sexps as possible (0>). At this point you have marked the stuff that you want killed or deleted. Press C-w or C-d appropriately.

  • Thanks for the suggestion. I think I'll try it out, although I harbor distaste for modal editing (even lispy's partial modal editing seems a bit too much for me). – GDP2 Jan 3 '17 at 20:54
0

Good question. I've come up with this (still, first) draft. As queried it's about killing line backward from point including keeping sexp balance as paredit does watch for it.

(defun paredit-kill-backward-from-point ()
     (interactive)
     (let ((prev-pos (point)))
       (paredit-backward-up)
       (paredit-forward-down)
       (kill-region (point) prev-pos)))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.