Longer version of question: how can I efficiently detect and indicate when emacs is dictation safe?
By "dictation safe", I really mean "when can type random printable ASCII characters on my keyboard, and know that they will typically self-insert, or at least not invoke random destructive commands like deleting files".
"Indicate" - for my purposes, that means putting some sort of special text or other marking in the emacs frame title / OS window title. That's trivial, you don't need to answer that part, unless you have a better suggestion that applies to my use case is explained below.
"Efficiently detect" means, well exactly that. I can scan the keyboard map looking for self-insert-command, but I certainly don't want to do this on every keypress. I only want have to do this when a new mode, whether major or minor, is loaded that I don't know about, or when there have been other things that change the keyboard mappings.
More below, including some places where looking for self-insert-command is insufficient.
Why am I asking about "dictation safety"?
As you might guess, I use speech recognition and voice control on my computer because of RSI, and I'm a very long time user of emacs. Currently I am using both Dragon and the latest thing, Talon Voice, which has enabled some things that Dragon was unable to do by providing more control over dictation.
Most speech recognition systems control their target applications by emulating keyboard and mouse events. Most speech commands are "open-loop" getting very little feedback from their target applications, although you can do things like inspect window sizes, and sometimes look ar file contents. Microsoft has a speech API, but even their own applications do not consistently use. Almost none of the applications that I use use SAPI. The most common way to provide a bit of context for speech commands is to look at the window title, as well as what application owns the window.
One of the most annoying things for voice control is when you are controlling applications that have key-bindings to unmodified printable ASCII keys. Ctrl+Alt modified key-bindings are not so bad, because they don't arise in dictation. But if I am in an emacs buffer/mode such as DirEd or BufferMenu, where many if not nearly all ordinary letters have non-self-insert key-bindings, if I accidentally dictate something lots of stuff can go on. Including sometimes deleting files.
Most if not all speech recognition systems have separate command and dictation modes. Most speech recognition systems also have a mixed mode: if an utterance is recognized as a command that is handled, else it is handled as dictation.
So why not just put myself in command mode when I'm in such an emacs buffer? Well, I switch between text and programming language buffers and buffer menu and dired a whole lot. Sometimes I forget. And in any case, having to the command over and over again is really annoying.
I have put the major mode in the frame title, and with Talon Voice I can enable/disable dictation according to the window title. I am using it for DirEd and BufferMenu. But emacs has many, many modes, I use many of them, and I want to be able to use more without tripping over myself every time I encounter something that is not dictation friendly. I was motivated to post this question, and actually to switch to Talon Voice, the umpteenth time I had to add another new major mode to my white/and/or blacklists of dictation safety.
Enough motivation, on to how to accomplish this
Scan keymaps for self-insert-command
Actually, rather than scan all the keymaps, I just loop over a-zA-Z0-9, the punctuation characters, and various whitespace characters, looking for (keybinding…) == self-insert-command.
If there are no self-insert-command findings found by this scan, it's likely not dictation safe. Although see below. Note however that many modes pass through to global bindings for the range \200 .. 3FFF7F as self-insert-command, which covers a whole slew of unicode code points. (I may have enabled some special unicode support to get this.) This motivates scanning for only the printable ascii characters.
BTW, ChatGPT suggests using map-char-table or standard-case-table -- but I don't think these are guaranteed to contain all printable input characters. E.g. not all characters need case conversion.
But when do I do this scan? Obviously not every character (I've tried).
What I really want is a place that I can hook or defadvice
- essentially when anything happens that might change what pressing a key X will do
- e.g. on every mode change - both major and minor modes
- possibly whenever a new key-binding is made
- e.g. using define-key
- but IIRC it is still possible to modify the keymap data structures by hand
- when the set of active keymaps is changed
Unfortunately I can't see any conveniently relevant hooks.
Modes that redefine self-insert away
Org-mode has keymap entry ` org-self-insert-command'
Similarly when you make a dired buffer writeable, you get ` wdired--self-insert'
=> The presence of this remap entry is a pretty good indication that self-inserts are allowed to some extent, and hence it's probably dictation safe.
But of course this doesn't need to be done: some modes are older than entries, or at least may have been written by somebody who didn't know about entries, so they may have overridden any self-insert-command bindings without leaving any obvious clue.
I doubt that much can be done about this, but I thought I would ask anyway.
Minibuffer Modes
Minibuffer modes have their own keymaps. E.g. and incremental search binds printable characters like a-z to isearch-printable-char (I assume through the usual …).
But I will probably take the easy way out, and just assume that all minibuffers are dictation safe.
Text Properties and Overlays
It is my understanding that text properties and overlays can be associated with keymaps and key-bindings. Again, easy enough to scan, but the question was how to avoid repeatedly scanning. Are there any hooks that can be invoked when a text property or overlay changes?
Conclusion
Looping over the printable characters a-zA-Z0-9 etc. and using (key-binding…) is straightforward.
But doing this constantly, e.g. whatever emacs' point changes, is rather a lot of work.
I sure would like to find a set of hooks or defadvice points that could make this more efficient.