reddit / emacs

How to show (temporary) cheat-sheet buffer

theldoria · 2022-02-09 11:39
I currently develop a little tool in which I would like to provide a temporarily visible help buffer containing cheat-sheet like content. And that content might be actually huge, so the user should be able to navigate the buffer. I plan to provide user options how and where that cheat-sheet might be shown (own frame, single windows with restoring `current-window-configuration` after close, side window left, right, top, bottom). My personal preference would be to open a new bottom window, as which-key does this for keybindings. The cheat-sheet mode would org, probably with a minor mode so that it is read only and can be closed with a single keystroke. I even think about using `org-view-mode`... Now my question is, what would be the best way to create and place the buffer? Shall I do it manually using basically `split-window`? Or is there something usable, probably the `*Help*`-buffers functionality as an official api? Or is it better to just create and show the buffer and let the user configure how to place it via `display-buffer-alist`?
akirakom · 2022-02-09 15:11
There are several frameworks for sidebar stuffs: treemacs, nano-sidebar, etc. If you don't mind opt-in, one of those can be a good choice.
mrolivertaylor · 2022-02-09 16:05
I recently wrote about this! https://www.olivertaylor.net/emacs/quick-help.html
codethrasher · 2022-02-09 17:19
Great article!
Aminumbra · 2022-02-09 18:10
There is also the somewhat similar, but built-in, `with-electric-help` function.
theldoria · 2022-02-09 18:46
That's an interesting approach. Thanks for sharing. As I understand this, despite the macro definition, boils down to using `pop-to-buffer` and locally defined keybindings instead of using a mini minor-mode.
mrolivertaylor · 2022-02-09 18:56
That's correct. I use `pop-to-buffer` so that I can control exactly how it is displayed with the `display-buffer` framework. It might make sense to bind the keys in a minor mode, or use `special-mode-map`.
theldoria · 2022-02-09 19:57
Good to know. Nice and easy way to show text only help. This comes with it's own major mode and rules out org mode, though.
theldoria · 2022-02-09 20:02
Thanks for your suggestion. I think treemacs is by far too heavyweight for that and nano-sidebar, although promising, is not on melpa (an I want my tool once to land on melpa, so probably dependencies should either be build-in or available on melpa).
theldoria · 2022-02-09 20:31
I noticed that this has a flaw. Using ```read-only-mode``` activates View mode, which should not be used from lisp according to the documentation string. This View mode activation in turn leads to a different binding of `q` as expected, its not bound to `kill-buffer-and-window` but `View-quit`. Quitting the buffer may quit it, but it is not killed. Trying to re-open the buffer and inserting text into it then fails, because it is still read-only (and filled with old content). This can be avoided with: (let ((view-read-only nil)) ;; Do not use view-mode (read-only-mode 1)) Also your code uses `not-modified`, but it might be better to use `(set-buffer-modified-p nil)`. And one last thing: it saves some memory if you use `(buffer-disable-undo)` before inserting text. All in all, what I came up with is: (defun my-quick-help (topic text) "Create quick help buffer for TOPIC with TEXT in org mode." (let ((qh-buffer (concat "*Quick Help: " topic "*"))) (with-current-buffer (get-buffer-create qh-buffer) (buffer-disable-undo) (setf (buffer-string) text) (set-buffer-modified-p nil) (goto-char (point-min)) (org-mode) (toggle-truncate-lines 1) (let ((view-read-only nil)) ;; Do not use view-mode (read-only-mode 1)) (local-set-key (kbd "q") #'kill-buffer-and-window)) (pop-to-buffer qh-buffer '((display-buffer-below-selected) (window-parameters . ((no-other-window . nil))) (window-height . fit-window-to-buffer))) (message "q - Remove Window"))) Your macro could then become: (defmacro def-quick-help (name buffer text) "Macro for creating callable functions that display help. Where NAME is name of function, BUFFER is name of buffer, and TEXT is displayed." (declare (indent defun)) `(progn (defun ,name nil ,buffer (interactive) (my-quick-help ,buffer ,text))))
mrolivertaylor · 2022-02-09 21:23
Good ideas! Thanks. Question: why separate the function from the macro? Also, I've since updated the macro a little to add each callable function to an alist and changed the binding to this: (defun quick-help-prompt nil "Prompt user for help to display." (interactive) (call-interactively (intern (completing-read "Quick Help: " quick-help-functions-alist)))) (defkey global-map "C-c h" 'quick-help-prompt) That way I can have dozens of them and not need individual bindings.
theldoria · 2022-02-09 21:32
> Question: why separate the function from the macro? Well, to be honest I do not se much benefit in the macro. Compared to conventional defined functions it only saves me two lines to define the help function. And, as you wrote your macro, the description only has the string in buffer as documentation string. Furthermore, I will get the help content from (a part of) a file. So my help function reads that and calls `my-quick-help`. But to really answer your question: having as much functionality as possible in conventional functions makes it much easier to test them. So I always try to minimize macro code.
theldoria · 2022-02-09 21:41
The alist is a nice idea and that, of course, makes using the macro more useful as well.
oantolin · 2022-02-09 21:59
Nope, org-mode works just fine: (with-electric-help (lambda () (org-mode) (dotimes (_ 10) (princ (format "%s Some heading!\n" (make-string (1+ (random 3)) ?*))))))
oantolin · 2022-02-09 22:03
Here is the same code reformatted with some linebreaks and indentation, in case it proves too hard to read when it's all on one line: (defun my-quick-help (topic text) "Create quick help buffer for TOPIC with TEXT in org mode." (let ((qh-buffer (concat "*Quick Help: " topic "*"))) (with-current-buffer (get-buffer-create qh-buffer) (buffer-disable-undo) (setf (buffer-string) text) (set-buffer-modified-p nil) (goto-char (point-min)) (org-mode) (toggle-truncate-lines 1) (let ((view-read-only nil)) ;; Do not use view-mode (read-only-mode 1)) (local-set-key (kbd "q") #'kill-buffer-and-window)) (pop-to-buffer qh-buffer '((display-buffer-below-selected) (window-parameters . ((no-other-window . nil))) (window-height . fit-window-to-buffer))) (message "q - Remove Window"))) (defmacro def-quick-help (name buffer text) "Macro for creating callable functions that display help. Where NAME is name of function, BUFFER is name of buffer, and TEXT is displayed." (declare (indent defun)) `(progn (defun ,name nil ,buffer (interactive) (my-quick-help ,buffer ,text))))
mrolivertaylor · 2022-02-10 00:04
Good point.
ShadowRylander · 2022-02-10 00:49
I actually wrote a package to recursively turn a keymap into a hydra, called [meta](https://github.com/shadowrylander/meta); pass it a keymap, and it'll turn it into a hydra, but how it works, I have no idea, and I'm a little scared to find out. 😹
theldoria · 2022-02-10 05:18
Oh, strange, why was it all in one line for you?
theldoria · 2022-02-10 05:19
Ah, yes, of course, I can simply switch the mode in the provided function.
oantolin · 2022-02-10 05:37
I think you must have used the triple-backquote syntax for code blocks that GitHub-Flavored Markdown uses. Reddit does not support it universally, I think that works on reddit.com and maybe a few mobile apps, but it does not work on old.reddit.com nor on some other mobile reddit apps. If you use original Markdown syntax, which is to indent each line with four spaces, that should work on all versions of reddit.
theldoria · 2022-02-10 06:53
Yes, that's what I did, wasn't aware of that issue. I will remove my triple backquotes.
publicvoit · 2022-02-11 11:45
[In my setup](https://github.com/novoid/dot-emacs/blob/master/config.org), this is solved by manually written [hydras](https://github.com/abo-abo/hydra). They consist not only of commands to invoke but also simple text that reminds me of standard bindings.