drym-org / symex.el

An intuitive way to edit Lisp symbolic expressions ("symexes") structurally in Emacs
Other
270 stars 22 forks source link

Feature request: Detect "mode" for org code blocks? #95

Open devcarbon-com opened 1 year ago

devcarbon-com commented 1 year ago

Use case: quickly tinkering in literate programming. A common case being a literate emacs config.

countvajhula commented 1 year ago

Could you give an example of what you're looking for here?

devcarbon-com commented 1 year ago

Sure,

Say you have a literate config with the following:

#+begin_src emacs-lisp
(setq mouse-autoselect-window t)
#+end_src

As-is, if I try to eval the setq, I get symex--evaluate: Symex mode: Lisp flavor not recognized!

It would be neat if Symex would detect it was in an org src-block and use the set language for evaluation.

devcarbon-com commented 1 year ago

Along these lines:

(when (eq major-mode `org-mode)
    (let ((element (org-element-at-point)))
      (when (eq 'src-block (org-element-type element))
        (org-element-property :language element))))
countvajhula commented 1 year ago

Ah yeah, that would be nice.

Here's one way we could do it:

We first define a general notion of cursor context awareness. This could be as simple as a (set-context category) function that checks a list of (condition, handler) pairs for the given category, where if the condition is met then the handler is called. The purpose of the "category" is to specialize the context-handling to specific cases, for instance, "symex" would be one category here which would be used by Symex users and the Symex library, where (set-context symex-context) would replace (and generalize) the current major mode Lisp flavor check that you ran into. But other libraries could use different categories so that they don't interfere with one another (so this would be a separate library and not part of Symex itself). Users could add context handlers using a function like (context-add-rule category condition handler).

For the org case and Symex, we would do:

(context-add-rule symex-context #'my-org-inside-literate-code-block symex-set-language-context)

We could implement context-add-rule so that the handler is called with the return value of the condition predicate. That way, here, myorg-inside-literate-code-block could return "python" (i.e. it would need to determine that by parsing the containing org text in any way that works) and that would become the input to setting the language context.

There could be another helper package called cursor-context-predicates which includes a bunch of standard predicates (detecting literate org code blocks, detecting comment blocks, or whatever else) so that users wouldn't need to write ELisp for standard cases.

For the case of comments, we could register a comment detection predicate and call set-context in the high level symex motions (h and l) so that encountering a comment would trigger the use of a different "symex-comment-mode" or something like that, which would allow going "down" to the base of the comment, and "up" into the body of the comment.

This could also be used in Rigpa to set the appropriate editing ensemble (i.e. data structure like a tower containing editing states like Evil states) based on whether we're in a comment or not, which is a feature I've been missing for a while.

If you think this is a good approach, you could try writing the my-org-inside-literal-code-block function -- it should return something like "elisp" or "python" if it is in a literal code block, and nil otherwise. In the meantime, I'll write an early prototype for the context handling as a distinct Symex module, which just refactors the existing major mode checks to use the context-setting pattern. Then we should be able to extend it to support the other cases here with predicates for each case, like the org-literal-code-block predicate.

devcarbon-com commented 1 year ago

@countvajhula Neat!

On a side note I'm curious, is the plan for Rigpa to also be evil independent at some point?

countvajhula commented 1 year ago

Do you mean from the perspective of users or just as a dependency? As far as I know, both Rigpa and Symex are independent of whether users are Evil users or not. I haven't reviewed this in detail recently so it's likely that the interface with Evil can be further minimized, but from the perspective of users it's just an implementation detail that should not affect them. If it does affect users by revealing the inner Evil workings or expecting Evil familiarity in some way, then I would consider it a bug (please report any such case! 🙂 ).

countvajhula commented 1 year ago

Regarding whether there is a plan to not even use evil as a dependency (for either Rigpa or Symex), yes, I think that would make sense in the long term since Evil is much more heavyweight than our actual needs. But in the near term a higher priority is countvajhula/rigpa#10 -- that is, writing a modal interface provider that, unlike evil, isn't tied to a specific buffer, and unlike hydra, is specifically designed to be lightweight modal interface. This would probably replace hydra-backed modes (like View, Window, Buffer) rather than affect evil-backed modes (like Line, Word, Symex), for now. But it could potentially replace evil-backed modes too. I think a great proof-of-concept would demonstrate its use as View Mode (for example) without some of the hacks that are currently necessary to accommodate hydra exits (i.e. currently we need to advertise that the hydra is "possibly about to exit" beforehand, and then later check whether it did in fact exit via a different hook -- see "portend-exit" and "signal-exit" in the rigpa code if you're curious -- but understanding that is beside the point since we would like to avoid that whole way of doing it!). Of course, we would still support hydra for the purposes of abstraction, but all built-in Rigpa modes would be transitioned to the new provider (or remain on evil).

devcarbon-com commented 1 year ago

I haven't dug into this code specifically yet, but boon-core sounds like it might be good inspiration if nothing else for a rigpa mode provider: https://github.com/jyp/boon#comparison-with-other-modal-layers-for-emacs