Decompose symex.el into modular packages
For instance:
symex(this package) becomes a package containing only the DSL syntax and semantics, i.e. the language together with its implementation in terms of primitives- A new package
symex-evilthat contains the evil modal interface frontend - A new package
symex-hydrathat contains the hydra modal interface frontend - A new package
symex-runtimethat interfaces with modes for each Lisp flavor to manage the execution environment (e.g. REPL, evaluation, documentation lookups, scratch buffers, etc.).
At the moment all of the above functionality is present in the current package.
Possible benefits of decomposing it:
- developers could use the core DSL to power Lisp-related features in other libraries, without needing to inherit the modal interface dependencies
- users could use one or the other modal interface frontends (i.e. evil or hydra) without inheriting both dependencies [although, at the moment evil is a hard dependency since some features are implemented using evil functions for convenience -- this reliance in the core could be reduced and eventually eliminated, though]
- users who only need the structural features provided by the DSL need not inherit the keybindings for the runtime integrations (e.g. REPL, ...) -- see #9 .
[Extracted from #20 ]
I'm interested in using symex with meow.
Ideally, there would be no evil dependancy at all - (and I'd be happy to help with that as I'm able) but for now I'm curious: do you have to "use" evil to have symex?
Ah - found this in the readme:
is available to both evil and vanilla emacs users.
Yeah, it would be great to eliminate the evil dependency in the core symex package after decomposition (and as you found, even currently you don't need to be an Evil user to use Symex). Regarding helping with this, it would amount to finding every instance of use of an evil API in symex-transformations.el and symex-misc.el (since those are part of the core code and not specific to the modal interface like symex-evil.el) and replacing it with vanilla emacs code that does the same thing. As I recall, the only reason we use Evil in these modules is because it was convenient to use evil interfaces to do things I already knew how to do in evil/Vim (e.g. deleting lines, moving the cursor, etc.), so it was expedient to use them.
Given the exciting work going on with tree-sitter integration, I think it's key to separate out evil as "one of many usage scenarios" rather than leading with it.
When I was evaluating sexp editors, I skipped right over symex after reading the first few words (since I'm not an evil user). I settled on lispy, and really like its idea of "one-key modality at the boundaries of an sexp". Maybe that style could be implemented in another interface "sub-package". Ideally you can mix and match: boundary modality for these edits/motions, modal prefix for these others, regular Emacs keybindings for some, and a hydra/transient pop-up as backup.
FWIW even today, I bet it wouldn't be too hard to use Symex and Lispy together though it would be a hacked-together integration.
But I definitely agree with you that the UI (e.g. modal interface vs boundary modality vs no modality) should be separated from the functionality (e.g. the DSL/traversal language, the primitive structural operations, etc.) so that they can be mixed and matched as desired in a clean way.
So I have a (very rough) draft of this I was working on as a pull request, but something got really messed up in git somehow. I'm including it here in case it is useful to you, as it may be a long time before I could polish this up myself. So far all the evil refs are replaced, except for evil-surround in symex-transformations.el
Most of what still needs to be done is namespacing and testing. draft.txt
I appreciate your efforts! At this point, it would be most useful to make a pull request against the 2.0-integration branch rather than starting from master, as there is a lot of work in progress there that has changed much of this code (e.g. Lispy is no longer a dependency, and the evil footprint has been reduced, too).
Still, if you are not able to get to a PR, if you wouldn't mind summarizing your changes in bullet points, that would help me to know if there is something in your changes that I should use and avoid redoing any work.
Glancing at the new symext-transformations.el I think these functions are the remaining relevant ones:
(defun --surround-change (delimiter)
"Change current delimeters to match DELIMITER."
(let ((bounds (bounds-of-thing-at-point 'sexp)))
(--surround (car bounds) (cdr bounds) delimiter)))
(defun --join (beg end)
"Join the selected lines."
(let ((count (count-lines beg end)))
(when (> count 1)
(setq count (1- count)))
(goto-char beg)
(dotimes (_ count)
(join-line 1))))
(defun --delete-pair ()
"Delete a pair of characters enclosing sexps that follow point."
(save-excursion
(skip-chars-forward " \t")
(save-excursion
(let ((open-char (char-after)))
(forward-sexp arg)
(unless (member (list open-char (char-before))
(mapcar (lambda (p)
(if (= (length p) 3) (cdr p) p))
insert-pair-alist))
(error "Not before matching pair"))
(delete-char -1)))
(delete-char 1)))
Nice, these look like they will be useful in shedding the evil-surround dependency 👍 . If you are interested, I can assign #106 to you. Otherwise, I'll aim to get to it and will plan to use this code.
@countvajhula Apologies for the delay. Sure, feel free to assign it. :)
@devcarbon-com Done!