More Paredit-like functions
Closed this issue · 6 comments
Hi! Thanks for a very interesting package!
I've been trying to replace my smartparens config with puni, and noticed that some Paredit-like functions were missing, so I've defined them myself. However, these functions seem essential so I wonder if you would like to accept them into the package itself.
(defun puni-splice-killing-backward ()
"Splice the list the point is on by removing its delimiters, and
also kill all S-expressions before the point in the current list."
(interactive)
(puni-soft-delete-by-move
#'puni-beginning-of-list-around-point)
(puni-splice))
(defun puni-splice-killing-forward ()
"Splice the list the point is on by removing its delimiters, and
also kill all S-expressions after the point in the current list."
(interactive)
(puni-soft-delete-by-move
#'puni-end-of-list-around-point)
(puni-splice))
These two commands are mapped to M-<up>
and M-<down>
respectively in Paredit, and I find them often more useful than plain puni-raise
, so I figured others coming from Paredit or Smartparens may want them.
Other ones are Paredit-like motions for C-M-f
and C-M-b
:
(defun puni-forward-sexp-or-syntactic-forward (&optional n)
"Go forward a sexp.
This is the same as `puni-strict-forward-sexp', except that it
jumps forward consecutive single-line comments, and will go over
syntactic boundaries.
With prefix argument N, go forward that many sexps. Negative
argument means go backward."
(interactive "^p")
(setq n (or n 1))
(if (< n 0) (puni-backward-sexp-or-syntactic-backward (- n))
(dotimes (_ n)
(or (puni-strict-forward-sexp 'skip-single-line-comments)
(puni-syntactic-forward-punct)))))
(defun puni-backward-sexp-or-syntactic-backward (&optional n)
"Go backward a sexp.
This is the same as `puni-strict-backward-sexp', except that it
jumps backward consecutive single-line comments, and will go over
syntactic boundaries.
With prefix argument N, go backward that many sexps. Negative
argument means go forward."
(interactive "^p")
(setq n (or n 1))
(if (< n 0) (puni-forward-sexp (- n))
(dotimes (_ n)
(or (puni-strict-backward-sexp 'skip-single-line-comments)
(puni-syntactic-backward-punct)))))
These will not completely stop on the delimiter, but go over it with the next invocation.
Another one that I miss from Paredit is wrapping the next expression via M-(
:
(defun puni-wrap-round (&optional n)
(interactive "P")
(insert-parentheses (if n n 1)))
If you're ok with these, I can create a pull request with them, or you can just take them from here, I don't mind.
Thanks! I have some suggestions on these commands. If you could fix them, please open a pull request; otherwise I could do it myself.
puni-splice-killing-backward/forward
The implementation looks good. In the docstrings, I think "the list around point" is better than "the list the point is on".
puni-forward/backward-sexp-or-syntactic-forward/backward
It looks good but is not doing the same thing as paredit-forward/backward
. For the same behavior like paredit-forward/backward
, use (puni-up-list)
to replace (puni-syntactic-forward-punct)
, and (puni-up-list 'backward)
to replace (puni-syntactic-backward-punct)
.
This matters in multi-char delimiters. In web-mode:
<p>test|</p>
;; Call puni-forward-sexp-or-syntactic-forward (your implementation)
<p>test<|/p>
<p>test|</p>
;; Call puni-forward-sexp-or-syntactic-forward (puni-up-list used)
<p>test</p>|
If we switch to the implementation using puni-up-list
, the commands also need to be renamed. Maybe puni-forward/backward-sexp-or-up-list
is appropriate. The docstring also needs to be revised.
puni-wrap-round
I may refuse this as
- It uses
forward-sexp
internally, notpuni-strict-forward/backward-sexp
, which is all about fixing the wrong behaviors offorward-sexp
. - With
electric-pair-mode
on, we can do this by simply mark the sexps, then pressing(
(or[
,"
...)
I've submitted a PR with your suggestions, and also fixed an unnoticed bug in puni-backward-sexp-or-syntactic-backward
implementation.
puni-wrap-round
I may refuse this as
* It uses `forward-sexp` internally, not `puni-strict-forward/backward-sexp`, which is all about fixing the wrong behaviors of `forward-sexp`. * With `electric-pair-mode` on, we can do this by simply mark the sexps, then pressing `(` (or `[`, `"`...)
No problem! I can keep this in my own config, it's just that I'm too used to the fact that just pressing M-(
will enclose the following sexp in parentheses without needing to do M-1 M-(
manually.
Although, maybe we could make a version that uses Puni's commands internally?
Although, maybe we could make a version that uses Puni's commands internally?
Yes. It's actually very easy ;)
(defun puni--wrap-region (beg end beg-delim end-delim)
"Wrap region between BEG and END with BEG-DELIM and END-DELIM.
The indentation of the region is adjusted to make it the same
like before wrapping. BEG and END are integers, not markers."
(save-excursion
(goto-char end)
(insert end-delim)
(goto-char beg)
(insert beg-delim)
(puni--reindent-region
(+ beg (length beg-delim))
(+ end (length beg-delim) (length end-delim))
(puni--column-of-position beg))))
(defun puni-wrap-round (&optional n)
(interactive "p")
(let* ((n (or n 1))
(from (point))
(to (puni-end-pos-of-list-around-point)))
(puni--wrap-region from to "(" ")")))
;; Test
(|(this is a sexp
that occupies 2 lines))
;; call puni-wrap-round
(|((this is a sexp
that occupies 2 lines)))
;; It handles multiline region correctly.
Do you want to put this (and maybe puni-wrap-[square|curly|angle]
) in your PR too?
Ah, I think I've written a puni-wrap-round
that does the complete wrong thing. It should be
(defun puni-wrap-round (&optional n)
(interactive "p")
(puni--set-undo-position)
(puni--wrap-region
(save-excursion
(puni--forward-blanks)
(point))
(save-excursion
(dotimes (_ (or n 1))
(puni-strict-forward-sexp))
(point))
"(" ")"))
You could also extract a tool function from this and implement puni-wrap-[round|square|curly|angle]
on it.
Wrapping is a bit harder than we thought, we should also respect an active region, as well as its direction, plus negative N
. Working on it