[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Adding a "quick-help" menu
From: |
Philip Kaludercic |
Subject: |
Re: Adding a "quick-help" menu |
Date: |
Thu, 13 Oct 2022 14:30:55 +0000 |
(Sorry for the delay, I got distracted)
Philip Kaludercic <philipk@posteo.net> writes:
> Stefan Kangas <stefankangas@gmail.com> writes:
>
>> Philip Kaludercic <philipk@posteo.net> writes:
>>
>>> Lars Ingebrigtsen <larsi@gnus.org> writes:
>>>
>>>> Philip Kaludercic <philipk@posteo.net> writes:
>>>>
>>>>> There is a fork of mg (MicroEmacs)[0] that binds C-h q to a command that
>>>>> pops up a buffer with these contents:
>>>>
>>>> I think having a "cheat sheet" like this would be useful.
>>>
>>> Calling it a "cheat sheet" sounds like a good idea. I've added
>>> `cheat-sheet' as an alias:
>>
>> Thanks, this looks great. Some comments after testing it:
>>
>> - I think we should use `help-for-help-header' for the headlines, not
>> capitalize them, and align them with :align-to.
>
> That can be done.
This has turned out to be more complicated than I had initially assumed,
because way the menu is constructed inserting special properties isn't
that easy. Do you think that this would be a hard requirement.
>> - Should it be modal, like `C-h C-h'?
>>
>> - If not, I think it would be nice if you could remove it by typing "q"
>> again.
>>
>> Perhaps point should be moved to the new buffer by default? (But then
>> again, it doesn't do that with "*Help*" by default, sadly.) So maybe
>> a repeat-map is appropriate? Just some ideas.
>
> That depends on how this is to be used. Is this something a new user
> would use to quickly peek if they don't know how to do something, or
> should it serve as a permanent "cheat sheet" at the bottom of the screen
> (sort of like Nano).
I really think that this should be a persistent menu, so closing the
menu with "q" seems like the wrong thing. Again, this is not a
which-key alternative, the target audience are people who are struggling
to remember how to open a file or kill a line.
>> - The keys should be linked to the corresponding command docstrings.
>
> This can also be done.
Has been added in the patch below.
>> - Wishlist: It would be great if it had an optional vertical view,
>> perhaps that you could toggle with both a command and defcustom.
>> Perhaps the vertical view could even be the default if the window
>> width is too narrow to fit it all?
>
> I was just thinking about this too. I implemented this vertically,
> which is more complicated, because that is what the mg fork did too, but
> mg has no horizontal splits, so it had no choice.
I haven't implemented this yet, because I am uncertain if it is worth
the additional complexity.
>> Also, some nits:
>>
>> - "other" should be "other win." or "other window".
>>
>> - "rev. search" should be "search backwards".
>>
>> - "replace" could be "search & replace" or "search&replace"
>
> We can try this, my only concern is that this might use too much
> horizontal space.
Also did this, it seems to be fine.
>From aa5e4b0e6f4aa60fed9c5b1dde3c590fc9a62e61 Mon Sep 17 00:00:00 2001
From: Philip Kaludercic <philipk@posteo.net>
Date: Sat, 17 Sep 2022 16:52:01 +0200
Subject: [PATCH] Add a quick-help menu
* lisp/help.el (help-map): Bind 'help-quit-or-quick' instead of 'help-quit'.
(help-quick-sections): Add variable.
(help-quick): Add main command.
(cheat-sheet): Add alias for 'help-quick'.
(help-quit-or-quick): Add auxiliary command.
---
lisp/help.el | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 133 insertions(+), 1 deletion(-)
diff --git a/lisp/help.el b/lisp/help.el
index b4b9120da3..1d960cf269 100644
--- a/lisp/help.el
+++ b/lisp/help.el
@@ -112,7 +112,7 @@ help-map
(define-key map "v" 'describe-variable)
(define-key map "w" 'where-is)
(define-key map "x" 'describe-command)
- (define-key map "q" 'help-quit)
+ (define-key map "q" 'help-quit-or-quick)
map)
"Keymap for characters following the Help key.")
@@ -125,11 +125,143 @@ global-map
(defvar help-button-cache nil)
+
+(defvar help-quick-sections
+ '(("File"
+ (save-buffers-kill-terminal . "exit")
+ (find-file . "find")
+ (write-file . "write")
+ (save-buffer . "save")
+ (save-some-buffers . "all"))
+ ("Buffer"
+ (kill-buffer . "kill")
+ (list-buffers . "list")
+ (switch-to-buffer . "switch")
+ (goto-line . "goto line")
+ (read-only-mode . "read only"))
+ ("Window"
+ (delete-window . "only other")
+ (delete-other-windows . "only this")
+ (split-window-below . "split vert.")
+ (split-window-right . "split horiz.")
+ (other-window . "other window"))
+ ("Mark & Kill"
+ (set-mark-command . "mark")
+ (kill-line . "kill line")
+ (kill-ring-save . "kill region")
+ (yank . "yank")
+ (exchange-point-and-mark . "swap"))
+ ("Projects"
+ (project-switch-project . "switch")
+ (project-find-file . "find file")
+ (project-find-regexp . "search")
+ (project-query-replace-regexp . "search & replace")
+ (project-compile . "compile"))
+ ("Misc."
+ (undo . "undo")
+ (isearch-forward . "search")
+ (isearch-backward . "reverse search")
+ (query-replace . "search & replace")
+ (fill-paragraph . "reformat"))))
+
+(declare-function prop-match-value "text-property-search" (match))
+
+;; Inspired by a mg fork (https://github.com/troglobit/mg)
+(defun help-quick ()
+ "Display a quick-help buffer."
+ (interactive)
+ (with-current-buffer (get-buffer-create "*Quick Help*")
+ (let ((inhibit-read-only t) (padding 2) blocks)
+
+ ;; Go through every section and prepare a text-rectangle to be
+ ;; inserted later.
+ (dolist (section help-quick-sections)
+ (let ((max-key-len 0) (max-cmd-len 0) keys)
+ (dolist (ent (reverse (cdr section)))
+ (catch 'skip
+ (let* ((bind (where-is-internal (car ent) nil t))
+ (key (if bind
+ (propertize
+ (key-description bind)
+ 'face 'help-key-binding)
+ (throw 'skip nil))))
+ (setq max-cmd-len (max (length (cdr ent)) max-cmd-len)
+ max-key-len (max (length key) max-key-len))
+ (push (list key (cdr ent) (car ent)) keys))))
+ (when keys
+ (let ((fmt (format "%%-%ds %%-%ds%s" max-key-len max-cmd-len
+ (make-string padding ?\s)))
+ (width (+ max-key-len 1 max-cmd-len padding)))
+ (push `(,width
+ ,(propertize
+ (concat
+ (car section)
+ (make-string (- width (length (car section))) ?\s))
+ 'face 'bold)
+ ,@(mapcar (lambda (ent)
+ (format fmt
+ (propertize
+ (car ent)
+ 'quick-help-cmd
+ (caddr ent))
+ (cadr ent)))
+ keys))
+ blocks)))))
+
+ ;; Insert each rectangle in order until they don't fit into the
+ ;; frame any more, in which case the next sections are inserted
+ ;; in a new "line".
+ (erase-buffer)
+ (dolist (block (nreverse blocks))
+ (when (> (+ (car block) (current-column)) (frame-width))
+ (goto-char (point-max))
+ (newline 2))
+ (save-excursion
+ (insert-rectangle (cdr block)))
+ (end-of-line))
+ (delete-trailing-whitespace)
+
+ (save-excursion
+ (goto-char (point-min))
+ (while-let ((match (text-property-search-forward 'quick-help-cmd)))
+ (make-text-button (prop-match-beginning match)
+ (prop-match-end match)
+ 'mouse-face 'highlight
+ 'button t
+ 'keymap button-map
+ 'action #'describe-symbol
+ 'button-data (prop-match-value match)))))
+
+ (help-mode)
+
+ ;; Display the buffer at the bottom of the frame...
+ (with-selected-window (display-buffer-at-bottom (current-buffer) '())
+ ;; ... mark it as dedicated to prevent focus from being stolen
+ (set-window-dedicated-p (selected-window) t)
+ ;; ... and shrink it immediately.
+ (fit-window-to-buffer))
+ (message
+ (substitute-command-keys "Toggle the quick help buffer using
\\[help-quit-or-quick]."))))
+
+(defalias 'cheat-sheet #'help-quick)
+
(defun help-quit ()
"Just exit from the Help command's command loop."
(interactive)
nil)
+(defun help-quit-or-quick ()
+ "Call `help-quit' or `help-quick' depending on the context."
+ (interactive)
+ (cond
+ (help-buffer-under-preparation
+ ;; FIXME: There should be a better way to detect if we are in the
+ ;; help command loop.
+ (help-quit))
+ ((and-let* ((window (get-buffer-window "*Quick Help*")))
+ (quit-window t window)))
+ ((help-quick))))
+
(defvar help-return-method nil
"What to do to \"exit\" the help buffer.
This is a list
--
2.37.3
- Re: Adding a "quick-help" menu,
Philip Kaludercic <=