|
From: | Noah Lavine |
Subject: | Re: Emacsy: Context Sensitive Commands Design Question |
Date: | Thu, 29 Aug 2013 20:26:31 -0400 |
Hello guilers,
I'm developing Emacsy[1], and I'm trying to design a good way of grouping and exposing interactive commands. My interest is specifically related to Emacsy, but for guilers in general the question is, How should one provide exports of a different flavor? We have only "public" exports. In my case I want a "command" flavor of export. But I might want to export "customizable" variables, or "minor-mode"s. I'm not trying to tackle the general task here.
In Emacs all commands are in the same namespace. Hit M-x TAB , and you'll probably see thousands of them (I've got ~6k). A lot of the commands may only be appropriate in certain modes, so I'm playing around with how best we can go about grouping and exposing commands. My idea is that M-x only shows commands that are in the current COMMAND_PATH, somewhat like a bash PATH. To show all commands, one can type C-u M-x.
A command in Emacsy is a procedure that requires no arguments. It's written for the user to invoke on command M-x or from a key binding. When called interactively, it'll ask the user for information. When called with all arguments provided, it will run non-interactively.
Suppose I have the following code which creates a command called 'echo':
(define-module (my-great-mode)
#:use-module (ice-9 optargs)
#:use-module (emacsy)
#:export (some-other-proc))
(define (some-other-proc)
"I am not a command."
#f)
(define* (echo #:optional (text (read-from-minibuffer "Echo what: ")))
(message text))
(export-command echo)
This module exports the procedure "some-other-proc" and I want it to _somehow_ export a command called "echo". The question is how to export this other thing, this command? I have a couple of ideas, most based off grouping some subset of procedures as commands inside a module, and using modules to group the commands.
1. Create a command-interface module similar to the public-interface that modules have. So instead of 'resolve-interface' and 'export', I'd have 'resolve-command-interface' and 'export-command'.
A (resolve-command-interface '(my-great-mode)) would return a module (%module-command-interface) that contained bindings for all that module's commands. This is the module that COMMAND_PATH would use for commands.
PRO: One could export commands and procedures entirely independently. Also, one can use the module mechanisms of selecting a subset, algorithmic renaming, etc. on these command modules.
CON: It seems like a lot of magic, but it's using the same mechanisms that public interfaces use.
2. Have (export-command) just keep a list of commands, but just export a custom public interface. So it might look like this internally:
(define (export-command-proc names)
(set! %command-set (append! %command-set names)))
(define (resolve-command-interface module-name)
(resolve-interface module-name #:select %command-set))
PRO: Less magic-y.
CON: One must export a command to both the public interface and the command; maybe that's ok even preferable that they're not independent.
3. Have (export-command) create a new sub-module (my-great-mode %command).
PRO: Not too tricky.
CON: Pollutes the module namespace a little bit.
4. Still use modules, but make the user segregate their procedures and commands into separate modules manually. So the user would have to define modules (my-great-mode) and (my-great-mode commands).
PRO: No magic. Programmer does everything.
CON: Everything is managed by convention. Separating the commands and procedures might be an awkward boundary. Consider an internal procedure in (my-great-mode) that a command wants to use in (my-great-mode commands). Do you expose it as public? Move it around? Use @@ to get to it?
5. Don't use modules at all. Make the user collect and manage <command-set>s the same way they manage keymaps, for instance.
PRO: Nothing tricky, doesn't use modules at all. No magic.
CON: It has to work with the command being redefined; we can't just keep the command procedure; we have to keep the variable which points to modules possibly being a better solution.
6. Tag commands as special procedures perhaps by adding something to their procedure properties. Implement a "command?" procedure. Export commands and procedures to the same module. Then just pluck the commands out of all the procedures by using command?.
PRO: No new modules. No new exports.
CON: Adding something to the procedure makes wrapping commands in other lambdas awkward. Might necessitate a define-command, lambda-command, method-command, which I'd prefer to avoid.
* * *
Thanks for indulging my scheme design question. What's the right thing to do? What would you do?
-Shane
[1]: https://github.com/shanecelis/emacsy
[Prev in Thread] | Current Thread | [Next in Thread] |