From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Philip Kaludercic Newsgroups: gmane.emacs.devel Subject: Re: Adding a "quick-help" menu Date: Thu, 13 Oct 2022 14:30:55 +0000 Message-ID: <87czav3ie8.fsf@posteo.net> References: <87v8pn4968.fsf@posteo.net> <87h7156lg5.fsf@gnus.org> <878rmhdkc9.fsf@posteo.net> <87fsgnfzb2.fsf@posteo.net> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="20208"; mail-complaints-to="usenet@ciao.gmane.io" Cc: Lars Ingebrigtsen , emacs-devel@gnu.org To: Stefan Kangas Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Thu Oct 13 16:33:01 2022 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1oizGf-00054p-8A for ged-emacs-devel@m.gmane-mx.org; Thu, 13 Oct 2022 16:33:01 +0200 Original-Received: from localhost ([::1]:53308 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1oizGe-0002f0-5Q for ged-emacs-devel@m.gmane-mx.org; Thu, 13 Oct 2022 10:33:00 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:43504) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oizEj-0001pL-I1 for emacs-devel@gnu.org; Thu, 13 Oct 2022 10:31:01 -0400 Original-Received: from mout01.posteo.de ([185.67.36.65]:45825) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oizEg-0002Y1-Se for emacs-devel@gnu.org; Thu, 13 Oct 2022 10:31:01 -0400 Original-Received: from submission (posteo.de [185.67.36.169]) by mout01.posteo.de (Postfix) with ESMTPS id B322E240028 for ; Thu, 13 Oct 2022 16:30:56 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.net; s=2017; t=1665671456; bh=aQ9k/++DtExqDNEhAEtZU5kuby23r78zpDgxiNuiWVg=; h=From:To:Cc:Subject:Autocrypt:Date:From; b=Ak2zm99NqwGawQ16saI3sQYzB8pBnFf6IfSKvL1hP3mosw6oTOPIKDAHGJHNxtl5c UxRezjtF0iPkKiXv7mc8YcKrwtTCaXp6U10IXCMCu8VyyjYK3mz3d/+YOX/6sRN1ep ijtYSvgp8Qc3RXfpwTBBisJsmkPSygo/qqhVnZIf9Xlv8C2BikKM+3GZOiO8HG479N bsAUw9IYzXj0LHdlTi3dPzRYWf1QRcatGUvKv4lQnGSwrwxZgvns/Td1Z+1JVCRPXK nqchn8qRUgVmDCGuH9Ur6AQxTE3DLlTrxZOhpDBZp+Ofvh4J0lQSHYMAhonHAGg0XJ 2HmpMAk11e4Qg== Original-Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4MpBlN0qWGz6tnc; Thu, 13 Oct 2022 16:30:55 +0200 (CEST) In-Reply-To: <87fsgnfzb2.fsf@posteo.net> (Philip Kaludercic's message of "Mon, 19 Sep 2022 12:12:17 +0200") Autocrypt: addr=philipk@posteo.net; prefer-encrypt=nopreference; keydata= mDMEYHHqUhYJKwYBBAHaRw8BAQdAp3GdmYJ6tm5McweY6dEvIYIiry+Oz9rU4MH6NHWK0Ee0QlBo aWxpcCBLYWx1ZGVyY2ljIChnZW5lcmF0ZWQgYnkgYXV0b2NyeXB0LmVsKSA8cGhpbGlwa0Bwb3N0 ZW8ubmV0PoiQBBMWCAA4FiEEDM2H44ZoPt9Ms0eHtVrAHPRh1FwFAmBx6lICGwMFCwkIBwIGFQoJ CAsCBBYCAwECHgECF4AACgkQtVrAHPRh1FyTkgEAjlbGPxFchvMbxzAES3r8QLuZgCxeAXunM9gh io0ePtUBALVhh9G6wIoZhl0gUCbQpoN/UJHI08Gm1qDob5zDxnIHuDgEYHHqUhIKKwYBBAGXVQEF AQEHQNcRB+MUimTMqoxxMMUERpOR+Q4b1KgncDZkhrO2ql1tAwEIB4h4BBgWCAAgFiEEDM2H44Zo Pt9Ms0eHtVrAHPRh1FwFAmBx6lICGwwACgkQtVrAHPRh1Fw1JwD/Qo7kvtib8jy7puyWrSv0MeTS g8qIxgoRWJE/KKdkCLEA/jb9b9/g8nnX+UcwHf/4VfKsjExlnND3FrBviXUW6NcB Received-SPF: pass client-ip=185.67.36.65; envelope-from=philipk@posteo.net; helo=mout01.posteo.de X-Spam_score_int: -27 X-Spam_score: -2.8 X-Spam_bar: -- X-Spam_report: (-2.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.io gmane.emacs.devel:297678 Archived-At: --=-=-= Content-Type: text/plain (Sorry for the delay, I got distracted) Philip Kaludercic writes: > Stefan Kangas writes: > >> Philip Kaludercic writes: >> >>> Lars Ingebrigtsen writes: >>> >>>> Philip Kaludercic 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. --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=0001-Add-a-quick-help-menu.patch >From aa5e4b0e6f4aa60fed9c5b1dde3c590fc9a62e61 Mon Sep 17 00:00:00 2001 From: Philip Kaludercic 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 --=-=-=--