reddit / emacs
quick-actions.el: Uniform Compile/Run/Debug across programming languages
jpgg2 · 2022-06-11 20:53
[quick-actions code (~40LOC) on GitHub](https://github.com/jeanphilippegg/quick-actions)
I see numerous posts asking how to compile or run a program with a single key.
Usually, the answers are always to bind a key to `compile` or some other custom function or to invoke a shell. These solutions have many disadvantages:
* The custom function is always very specific to a given project. The key bound to that function will not be useful in another project.
* The `compile` command (or custom function) allows only one action. Sure, you can type a different target (if you use `make`) on each `compile` invocation to run `main` or execute your tests, but it is quite limited. `projectile` provides 2-3 more commands. This is nice, but still limited.
* Once you solve the problem of compiling and running your code, you still have to know the specific ways to invoke the debugger and all languages have their specific invocations. You have to bind it to another key if you want to do it quickly.
* Invoking a shell is only an admission that Emacs cannot handle the task itself.
I propose the package **quick-actions** to solve the previous issues. It does not save you from having to learn how to run/compile/debug in all the various programming languages that you use, but once you do, you record that information in a function that you will be able to call with a single key (for example `<f5>`). This key will trigger a prompt to select the right action (Compile, Run, Debug, etc.) to perform. You have to configure this menu for each of your projects.
Improvements over the commonly suggested solution of binding keys to custom commands:
* The same key is used for all actions.
* The various actions are presented in a prompt (with details hidden).
* Only relevant actions are displayed in a given context.
___
Now that you have seen my package, I have a few questions:
- Is there another package that offers a similar functionality?
- I made `quick-action-alist` a safe variable with `:safe #'listp`. First, is it good practice to make this kind of variable safe in the first place? Second, `listp` might not be specific enough. How specific should it be?
- Any comments?
mickeyp · 2022-06-12 07:58
Although I'm sure the package is useful, there's a number of builtin methods of doing this, in addition to the millions of ways involving third-party packages:
1. You can set `compile-command` as a file or directory-local value.
2. You can `let`-bind `compile-command` in a custom `defun` if you want more than one `compile` command.
3. You can use a `Makefile` with a bunch of `.PHONY` targets; that's how I tend to manage this stuff. Now you can use completion in the prompt to run different targets. I have some hacky code that generates a hydra with all the targets so I can pick from a menu.
4. You can make a keyboard macro. Record `M-x compile` where you type in whatever it is you want to do -- bonus points for using `C-x q` (`kbd-query-flag`) to allow for user intervention -- and then name and commit the macro to a function with `insert-kbd-macro`. You can bind that to a key if you want; now you can have as many as you like. You can of course also go and run a `M-x shell` (or whatever) that runs the subsequent command.
entercaspa · 2022-06-12 09:23
\+1 for the Makefile approach, probably the most clean way to do so, especially if you are working on a project with multiple people, who might not be using emacs. Normalises standards for all involved.
Ghosty141 · 2022-06-12 11:37
For cmake there is also the cmakepresets file which I use at work. compile-commands + dir-locals + cmakepresets.json
jpgg2 · 2022-06-12 14:41
Thanks for your comment!
However, I am not sure how your 4 solutions avoid suffering from the disadvantages I mentionned in my OP.
I did not elaborate much on why I do not like solutions using the `compile` command. Ultimately, `compile` runs a shell command. What about languages that do not need to call a shell command to run the current script (like Python)? They just need to call an emacs lisp function. And what about invoking a debugger?
My package is an abstraction above all that. I do use `compile` for some of my actions, though, when appropriate.
If you know any builtin methods or packages, I am interested!
_viz_ · 2022-06-12 15:47
What exactly do you mean by your Python example? I'm sure you know already, but compile can run any arbitrary command and it even has a comint mode thingy (you need to give it the prefix arg iirc).
jpgg2 · 2022-06-12 16:12
Typically, to execute a script in Python, you use `python-shell-send-buffer` (bound by default to `C-c C-c`).
How do you get `compile` to satisfy my needs, as explained in my OP? For example, I want to compile in Debug and in Release. I also want to run my code and debug it sometimes and run the tests. How can I get `compile` to provide me with an interface to all those actions?
Sure, I can setup `compile` to run the code, then if I want to test, I can call `compile` again and manually type "pytest". I don't like this process. How can I get `compile` to perform multiple actions easily? Can compile call `gdb` itself?
My point is that `compile` has its limits. I have to remember all the different invocations and it only executes shell commands. I would rather learn them once, then forget the details. My package does that.
mickeyp · 2022-06-12 17:55
If you want to run arbitrary binaries, you can use `M-x comint-run`, or a more specialised command for that inferior process.
I'm not saying the tool isn't useful; just that `M-x compile` is literally made to run arbitrary stuff.
[deleted] · 2022-06-12 18:53
[deleted]
jpgg2 · 2022-06-12 19:25
Maybe I am missing something, but the doc of `compile` contains "Runs COMMAND, a shell command, in a separate process...", which means that it only runs shell commands, not arbitrary stuff. I also want to run `gdb` (the lisp command).
Anyway, the point of my package is to wrap all those invocations in a menu and that menu is not limited to shell commands only. It must really be able to contain arbitrary stuff (including elisp).
May I ask what is your personal workflow to accomplish those task? Do you program in Python or C++ for example? If so, how do you run a script on one hand and run your tests on the other and then debug? Do you have 1,2,3 bindings for these various tasks? What are they? Is it confortable? Is it not tiring to reach for all those invocations all the time?
Don't feel pressured to answer those questions, but I think the answers would be enlightening!
mickeyp · 2022-06-12 20:20
`compile` works fine and runs just about everything. Try it. If you want to use a third-party package to do this, go ahead. I merely outlined four ways of leveraging existing tooling in Emacs. As for what I do? I use Makefiles for common actions that I'd expect others to use also --- whether that is a human or a CI system. If I joined a team that used something else then that's what I'd point the likes of `compile` at. I do use other tooling that does not explicitly use `compile`, usually if I want to do something specific. I use something that calls out to pytest as it uses transient to give me magit-like argument flags.
That library, by the way, is like 40 lines of code (as you said) and it calls `compile` internally.
jpgg2 · 2022-06-12 21:58
How do you execute the elisp command `gdb` using `compile`? My package (I am the author of quick-actions.el btw) uses `compile` when appropriate for some actions, but not for others where `compile` is not the right tool (ie for calling elisp functions like `gdb`) as is exemplified in the README. As for Makefiles, they cannot contain elisp code.
And again, the problem I am trying to solve is to avoid having to remember specific invocations for various tasks, which also end up being different for all programming languages. My brain finds that heavy, annoying and distracting. I was just wondering if you were doing something better which I was not aware of. It seems not. You seem fine with it. It is possible that the issue is with my brain.
GuiB02 · 2022-06-13 00:47
Since you are asking, to give an example of a package that has some similar features, I would say ["quickrun.el"](https://github.com/emacsorphanage/quickrun) would be one.
jpgg2 · 2022-06-13 02:27
Thanks! I will check this out.
vfclists · 2022-06-13 04:27
Will a [hydra](https://github.com/abo-abo/hydra) or a [transient](https://github.com/magit/transient) menu?
mickeyp · 2022-06-13 07:31
Well, you can just run `gdb` (or whatever the command you want to run is). You can use a keyboard macro -- if you cannot write elisp -- to first call `compile`, then `gdb`, etc.
The invocation bit's fair enough, of course, but that is why many of us use `Makefile` for external stuff and, well, Emacsy stuff for Emacs commands.
Having said that, the great thing about Emacs is that you can do what you like.
_viz_ · 2022-06-13 14:47
IMO, that is the main problem with M-x compile. I would really prefer
it if Emacs had something like Acme's tag: imagine a mode-line just
like any other buffer where you can type in arbitrary text and execute
them, kind of like a toolbar but of free text. I'm currently trying
to recreate Acme tag in Emacs and nailed down the window layout.
For similar complaints about the Emacs model, see https://www2.lib.uchicago.edu/keith/software/emacs/packages/acme-readme.txt
jpgg2 · 2022-06-13 14:48
Thanks for the suggestions! They seem to provide similar functionalities.
jpgg2 · 2022-06-13 20:02
I don't know Acme. What you describe sounds like the `*scratch*` buffer. I must not see the whole picture. Once you have something working, I can look at it!