Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save mmarshall540/db04e510850703830ca114aeff33c497 to your computer and use it in GitHub Desktop.

Select an option

Save mmarshall540/db04e510850703830ca114aeff33c497 to your computer and use it in GitHub Desktop.
Alternatives to 'down-list' and 'backward-up-list' with string-movement, marking, and traversal
(defun my/down-sexp-with-mark (arg)
"Like `down-list', but with some differences.
- Enter strings in addition to lists.
- Mark inner of the list or string reached.
- Traverse beyond the lowest level in a list or string and descend
into the next top-level list or string when necessary.
- Move up and select contents of the string or list reached when
prefix is negative.
With a positive numeric ARG, repeat the down-list-like movement
ARG many times and mark contents of the string or list reached.
With the '- prefix or a negative numeric prefix, travel up the
surrounding lists and/or strings by (- ARG) levels, and descend
into the last escaped list or string. Thus, invoking the command
with a '- prefix will mark the inner content of the current
string or list."
(interactive "p")
(when (< arg 0)
(up-list arg t t)
(setq arg 1))
(if (eobp)
(message "At end of buffer, no more lists to descend into.")
;; Get out of any enclosing string first.
(when (nth 3 (syntax-ppss))
(skip-syntax-forward "^\"")
(while (= (char-before) ?\\)
(forward-char)
(skip-syntax-forward "^\""))
(forward-char))
(dotimes (i (or arg 1))
(unless (eq i 0) (forward-char))
(skip-syntax-forward "^(\"")
(unless (bobp)
(while (= (char-before) ?\\)
(forward-char)
(skip-syntax-forward "^(\""))))
(let ((start (point))
(end (save-excursion
(forward-sexp)
(point))))
(set-mark (1- end))
(goto-char (1+ start)))
(activate-mark)))
(keymap-global-set "C-M-d" 'my/down-sexp-with-mark)
(defun my/up-list-ad (&optional arg &rest _)
"Advise `up-list' so that if the mark is
active and we are moving around in lists, mark will move to the opposite
end of the s-expression to coincide with the new location of point."
(when (and (use-region-p)
(memq last-command '(my/down-sexp-with-mark
up-list
backward-up-list)))
(mark-sexp (if (< arg 0) 1 -1))))
(advice-add 'up-list :after 'my/up-list-ad)
@mmarshall540

mmarshall540 commented Nov 21, 2023

Copy link
Copy Markdown
Author

The my-down-sexp-with-mark command is an alternative to down-list (bound to "C-M-d") which enters strings in addition to lists. If it is already at the lowest level, it skips forward to the next list or string instead of stopping. It also marks the contents of the string or list it enters. If prefixed with a minus ('-) argument or -1, it selects the content of the list or string at point. If given an argument less than -1, it moves outward (like backward-up-list) before entering the list it reaches and selecting that list's contents.

The my-up-list-with-mark command is the same as backward-up-list (bound to "C-M-u"), except that it also marks the list or string it ascends out of.

@mmarshall540

Copy link
Copy Markdown
Author

Replaced my-up-list-with-mark, which I was no longer using, with some :after advice for the up-list function.

I had gone back to just using the built-in up-list and backward-up-list commands, since I found that marking wasn't always needed with them, and pressing C-g to deactivate the mark when moving up a list wasn't usually worth the trouble.

On the other hand, I found that often I would descend a list in order to reach one of the inner lists, and it was confusing to have the inner text of the list marked. When moving back up to the outside of the list, the mark remained at the end of the inner text, while point was moved to the outside of it.

This change allows me to move out of lists in the normal way, without activating the mark. But if the mark is already active, and I've just used the my/down-sexp-with-mark command, it will adjust the mark such that the whole list is marked.

@mmarshall540

Copy link
Copy Markdown
Author

Added keybinding for completeness.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment