unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: "Clément Pit-Claudel" <cpitclaudel@gmail.com>
To: Stefan Kangas <stefan@marxist.se>
Cc: Emacs developers <emacs-devel@gnu.org>
Subject: Re: Ideas to improve the output of C-h m?
Date: Fri, 1 May 2020 23:24:04 -0400	[thread overview]
Message-ID: <1aaad2a1-fcb3-d3fe-f434-b87acbd2f576@gmail.com> (raw)
In-Reply-To: <CADwFkmnCD4YXubOEDtc9E+kmTTgm-DW108gq121OjMoj_HBeoQ@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 2051 bytes --]

Hi Stefan,

On 01/05/2020 19.02, Stefan Kangas wrote:
> Thank you for working on this.  I like the look of the screenshot, but
> you didn't include a patch.  If you could provide one, I may be able
> to give you more useful feedback on the basis of testing it.

Thanks a lot for having a look.  I have attached the code; this version is extracted from biblio.el, for which it was originally written.  After loading it you should be able to use M-x help-with-major-mode and M-: (help-with-keymap occur-mode-map), for example.

There are shortcomings in the current implementation (it iterates over nested keymaps to flatten them, and that part is likely incomplete), including the fact that it displays <remap …> rather than actual keybindings when a keymap includes remapping.  All of these would have to be ironed out as part of turning this into a patch extending the existing facilities, of course.

> One related idea comes to mind: (…)> It would be useful, I think, if the mode author was able to customize
> the listing of commands while also "automatically" using the best
> standard for displaying them (with the added faces, indentation, etc.
> as you suggest) rather than having to write a doc string manually to
> achieve it.  For example, maybe we could come up with a way to group
> commands by categories and then have them displayed under proper
> headlines in key binding listings.

Yes, that would be great.  If the current discussion does get somewhere, it would be a natural next step to work on, I think.

> Footnotes:
> 1. As an aside, I have started converting 'substite-command-keys' from
> C to Lisp some time ago, but I got stuck somewhere and didn't complete
> it.  I hope I can find the energy and time to finish that job.

I think they may be some overlap with the code I posted, as I had to write much of the iteration over keymaps from scratch.  Maybe both implementations can benefit from each other and we can get something done together.  Or maybe I can be the first client of your new code :)

Cheers,
Clément.


[-- Attachment #2: help-with-major-mode.el --]
[-- Type: text/x-emacs-lisp, Size: 3852 bytes --]

(require 'seq)
(require 'pcase)

(defsubst biblio--as-list (x)
  "Make X a list, if it isn't."
  (if (consp x) x (list x)))

(defun biblio--map-keymap (func map)
  "Call `map-keymap' on FUNC and MAP, and collect the results."
  (let ((out))
    (map-keymap (lambda (&rest args) (push (apply func args) out)) map)
    (nreverse out)))

(defun biblio--flatten-map (keymap &optional prefix)
  "Flatten KEYMAP, prefixing its keys with PREFIX.
This should really be in Emacs core (in Elisp), instead of being
implemented in C (at least for sparse keymaps).  Don't run this on
non-sparse keymaps."
  (nreverse
   (cond
    ((keymapp keymap)
     (seq-map (lambda (key-value)
                "Add PREFIX to key in KEY-VALUE."
                (cons (append prefix (biblio--as-list (car key-value)))
                      (cdr key-value)))
              (delq nil
                    (apply
                     #'seq-concatenate
                     'list (biblio--map-keymap
                            (lambda (k v)
                              "Return a list of bindings in V, prefixed by K."
                              (biblio--flatten-map v (biblio--as-list k)))
                            keymap)))))
    ;; FIXME This breaks if keymap is a symbol whose function cell is a keymap
    ((symbolp keymap)
     (list (cons prefix keymap))))))

(defun biblio--group-alist (alist)
  "Return a copy of ALIST whose keys are lists of keys, grouped by value.
That is, if two key map to `eq' values, they are grouped."
  (let ((map (make-hash-table :test 'eq))
        (new-alist nil))
    (pcase-dolist (`(,key . ,value) alist)
      (puthash value (cons key (gethash value map)) map))
    (pcase-dolist (`(,_ . ,value) alist)
      (let ((keys (gethash value map)))
        (when keys
          (push (cons (nreverse keys) value) new-alist)
          (puthash value nil map))))
    (nreverse new-alist)))

(defun biblio--quote (str)
  "Quote STR and call `substitute-command-keys' on it."
  (if str (substitute-command-keys (concat "`" str "'")) ""))

(defun biblio--quote-keys (keys)
  "Quote and concatenate keybindings in KEYS."
  (mapconcat (lambda (keyseq)
               (let ((key (ignore-errors (help-key-description keyseq nil))))
                 (if (and nil key (string-match-p " " key))
                     (biblio--quote key)
                   key)))
             keys ", "))

(defun biblio--brief-docs (command)
  "Return first line of documentation of COMMAND."
  (let ((docs (or (ignore-errors (documentation command t)) "")))
    (string-match "\\(.*\\)$" docs)
    (match-string-no-properties 1 docs)))

(defun biblio--help-with-major-mode-1 (keyseqs-command)
  "Print help on KEYSEQS-COMMAND to standard output."
  ;; (biblio-with-fontification 'font-lock-function-name-face
  (insert (biblio--quote-keys (car keyseqs-command)) " ")
  (insert (propertize "\t" 'display '(space :align-to 10)))
  ;; (insert "(")
  (insert-text-button (format "%S" (cdr keyseqs-command)))
  ;; (insert ")")
  (insert "\n")
  (insert (propertize (format "  %s\n" (biblio--brief-docs (cdr keyseqs-command)))
                      'face '(font-lock-comment-face (:height 0.95))))
  (insert (propertize "\n" 'face '(:height 0.3))))

(defun help-with-keymap (map &optional buf title)
  "Display help for keymap MAP in buffer BUF, with a given TITLE."
  (setq buf (or buf "*Keymap help*"))
  (with-help-window buf
    (when title (princ title))
    (let ((bindings (nreverse (biblio--group-alist (biblio--flatten-map map)))))
      (with-current-buffer buf
        (seq-do #'biblio--help-with-major-mode-1 bindings)))))

(defun help-with-major-mode ()
  "Display help for current major mode."
  (interactive)
  (help-with-keymap
   (current-local-map)
   (format "*%S help*" major-mode)
   (format "Help with %s\n\n" (biblio--quote (symbol-name major-mode)))))

  reply	other threads:[~2020-05-02  3:24 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-01 14:34 Ideas to improve the output of C-h m? Clément Pit-Claudel
2020-05-01 15:55 ` Stefan Monnier
2020-05-01 16:16   ` Clément Pit-Claudel
2020-05-01 17:44     ` Stefan Monnier
2020-05-01 23:02     ` Stefan Kangas
2020-05-02  3:24       ` Clément Pit-Claudel [this message]
2020-05-06 13:34         ` Stefan Kangas
     [not found]         ` <jwv5zd9wyj6.fsf-monnier+emacs@gnu.org>
     [not found]           ` <CADwFkmmhezor_jCwff8YH2wVvnyb7Rf=7YnLeeN_aRehkw8beA@mail.gmail.com>
2020-08-21  0:27             ` scratch/substitute-command-keys: C conversion of s-c-k Stefan Kangas
2020-05-03  3:39       ` Ideas to improve the output of C-h m? Richard Stallman
2020-05-02  4:05 ` Jean-Christophe Helary

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1aaad2a1-fcb3-d3fe-f434-b87acbd2f576@gmail.com \
    --to=cpitclaudel@gmail.com \
    --cc=emacs-devel@gnu.org \
    --cc=stefan@marxist.se \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).