21

I'd like to display 3 lists of words on separate lines horizontally along the bottom (although top would work too) of every emacs frame I have open. I've thought of 6 ways to do this, and they all have issues:

  1. My first thought was to add a line to my mode line, but AFAICT you can't use the newline character in a mode line, it just gets converted to "^J".

  2. My second thought was to have the line across the top of the screen and use the header line, but it doesn't support the newline character either.

  3. I could display an overlay over the last 3 lines of the window, but making this robust seems hard -- scrolling would need to be triggered when point reached the overlay rather than the real end of the window, and I'd have to constantly reposition the overlay since overlays are in text space not window space.

  4. I could try to make dedicated windows at the bottom of the frame. I have tried coding this up but it's not very robust either, it doesn't seem to work right when a frame already contains split windows and I've had to rebind C-x,1 to a custom version of delete-other-windows that ignores my special windows and I'm sure there are other corner cases. Also when a help window pops open now it pops open vertically because it thinks there's already a horizontal split (which technically there is but it's only to display a one line window).

  5. I could have a dedicated frame for this, but then my config won't work in terminal mode, and I'd have to script my window manager to handle keeping it along the bottom of the screen, making it unselectable, not affecting layout, etc. etc.

  6. I could insert the text for the 3 lines directly into the minibuffer. I got this partially working, I can grow the minibuffer to accomodate the 3 lines, and I can display them. However, any time any message is echo'd the lines disappear until I issue another command at which point they re-appear. Ideally the 3 lines and the echo area would not overlap so I could see both. This would be less annoying if I could reliably filter which messages go to the echo area -- I found a solution on EmacsWiki but it doesn't appear to work for messages that originate in the emacs C source (specifically I'd like to get rid of the file saving messages because I autosave often on a timer).

For context, my goal is to constantly have a display of the most frequently used words in the current buffer, the words nearest point in the current buffer, and the words most recently used in the current buffer. I intend to be able to insert them into the buffer via voice commands. So I could say "nearest 2" and have it pick the second item from the list of words nearest point and insert it. I only care about the word lists being visible for whatever buffer I'm currently editing. I don't want to use the pop-up windows used by the various code completion modes because I need the lists always visible.

12
  • Good question, and well posed. Hope you get some useful suggestions.
    – Drew
    Commented Dec 19, 2014 at 23:53
  • I wonder about the other parts of #5, besides the terminal-mode need (which is major). You could use a dedicated frame that is positioned at the bottom (no problem). What do you mean by unselectable, and why do you need that? If you mean read-only, then that is no problem either. What do you mean by not affecting layout? In sum, #5 is not too clear to me.
    – Drew
    Commented Dec 19, 2014 at 23:57
  • @Drew: I mean that I drive my WM using the keyboard, and it's unlikely I would ever want to give that frame focus on purpose, so I'd want my next-window/previous-window bindings to skip over it. Likewise I'd want the window layouts to act as if that frame was just part of a panel/taskbar. Edit: everywhere in this comment I said 'window' I mean X window, not emacs window ;p Commented Dec 20, 2014 at 2:45
  • One other possibility suggested to me is just using the minibuffer. I have no idea if it's possible to passively display text down there without it interfering with everything else trying to use it though... Commented Dec 20, 2014 at 3:06
  • Added a note about trying to use the minibuffer. Commented Dec 20, 2014 at 5:20

2 Answers 2

8

With a lot of hacky experimentation I was able to get #6 (using minibuffer text) to a 'good enough' working state. Here's a screenshot:

Screenshot of belt in action

There are several key parts to make this work:

  • Inserting text into the minibuffer surprisingly almost does the right thing out of the box. Text inserted there will actually show up.
  • By making the text be the 'after-string of an overlay instead of regular text you make it unselectable and don't have to worry about the cursor accidentally getting in it.
  • To make minibuffer prompting commands work correctly, you have to inhibit inserting the text/overlay when the minibuffer window is active.
  • If you try to resize the minibuffer with normal window resize functions you will get errors about windows being too small, if you use the undocumented md-resize-minibuf function then you can resize to the exact number of lines you want, as long as you have set resize-mini-windows to nil first.
  • To solve the lists disappearing whenever there is a message you have to advise the message function to intercept messages. Then you insert them into the minibuffer yourself. You also have to look at the current-message variable, which stores whatever last showed up in the echo area (surprisingly the echo area and the minibuffer are technically distinct and some C source code functions print directly to the echo area without going through the message function). The code I provide below for this is imperfect, messages persist longer than they normally would which I still need to investigate (checking the last entry in *Messages* may be simpler and more robust) but this is 'good enough' for now.

Here's a link to my implementation with an example belt displaying the kill ring. Eventually this will be part of a proper project: https://gist.github.com/jgarvin/ce37d08654978fd7e4c9

This is my first time writing any significant quantity of elisp so quality is probably subpar, but it works.

1

Neither the mode-line nor the header-line can be multiple lines, unfortunately. I have asked about this before and there is (at least wasn't) any hidden option to make this work. So 1 and 2 are out. I also feel like 3 and 6 are hacks that won't make you happy in the long run. 3 and 4 seem like fine approaches, but to get them to work reliably will be quite an investment.

So I would recommend that you first bring this up on emacs-devel. In my experience things do get implemented eventually if you bother to carefully explain what you want and why that is a good thing. It might take some time, at least until the next release, but if you are fine waiting a bit or using the development version, then you could possibly get exactly what you want, with much less effort.

1
  • Thanks for the suggestion to contact emacs-devel. Even though I came up with a solution it was pretty hacky and it would be nice to have a real API for directly drawing on screen coordinates, so when I have the time I'll probably shoot an email their way. Commented Jan 13, 2015 at 19:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.