emacs-helm / helm

Emacs incremental completion and selection narrowing framework
https://emacs-helm.github.io/helm/
GNU General Public License v3.0
3.37k stars 390 forks source link

Improving `helm-info-*` #1324

Closed xiongtx closed 8 years ago

xiongtx commented 8 years ago

I really like the ability to quickly search through documentation with the helm-info-* commands. However, I think there are several issues with how the system currently works.

Too many commands, no unified interace

There are lots of individual commands for different info files like helm-info-elisp and helm-info-emacs and no way to easily choose the one you want. You can narrow down your choices with helm-M-x, but that takes a whole bunch of keystrokes.

What we need is a single, unified interface to all the helm-info-* commands. I whipped together something:

(defvar helm-info-searched (make-ring 32))

(defun helm-info-search-index (candidate)
  "Search the index of CANDIDATE's Info file using helm-info-<CANDIDATE>."
  (let ((helm-info-function
     (intern-soft (concat "helm-info-" candidate))))
    (when (fboundp helm-info-function)
      (funcall helm-info-function)
      (ring-insert helm-info-searched candidate))))

(defun helm-def-source--info-files ()
  (helm-build-sync-source "Helm Info"
    :candidates (sort (copy-sequence helm-default-info-index-list)
              #'string-lessp)
    :nomark t
    :action '(("Search index" . helm-info-search-index))))

(defun helm-info ()
  "Preconfigured `helm' for searching Info files indices."
  (interactive)
  (let ((default (ring-ref *-helm-info-searched 0)))
    (helm :sources (funcall #'helm-def-source--info-files)
      :buffer "*helm Info*"
      :preselect (and default
              (concat "\\_<" (regexp-quote default) "\\_>")))))

Can we get it more polished and included in helm?

Lots of commands don't actually work

Looking at helm-info.el, it seems that the helm-info-* commands are auto-generated by helm-define-info-index-sources, which looks at helm-default-info-index-list, a customizable list of info file names, to determine which commands to generate.

However, depending on the user's system, lots of commands don't actually work. helm-info-ratpoison, for example, does nothing because I don't have the corresponding Info file.

Although the list is customizable, I'm wondering if there is a way to automatically extract all available Info files so the user doesn't have to worry about it.

I know the dir file (e.g. "/user/share/info/dir") maintains a directory of Info files; might we have a customizable list of paths to dir files such that we can automatically detect which Info files are valid from these dir files?

tuhdo commented 8 years ago

:+1:

I agree it's much more convenient for a unified command, although it might take a bit longer but we should cache the index after the first invocation of the unified command.

thierryvolpiatto commented 8 years ago

Here your code slighly modified and a function to fetch info files. If it is working for you perhaps you can make a PR with this, (improve it if needed).

(defvar helm-info-searched (make-ring 32))

(defun helm-info-search-index (candidate)
  "Search the index of CANDIDATE's Info file using helm-info-<CANDIDATE>"
  (let ((helm-info-function
         (intern-soft (concat "helm-info-" candidate))))
    (when (fboundp helm-info-function)
      (funcall helm-info-function)
      (ring-insert helm-info-searched candidate))))

(defun helm-def-source--info-files ()
  (helm-build-sync-source "Helm Info"
    :candidates helm-default-info-index-list
    :candidate-transformer
    (lambda (candidates)
      (sort candidates #'string-lessp))
    :nomark t
    :action '(("Search index" . helm-info-search-index))))

(defun helm-info ()
  "Preconfigured `helm' for searching Info files indices"
  (interactive)
  (let ((default (unless (ring-empty-p helm-info-searched)
                   (ring-ref helm-info-searched 0))))
    (helm :sources (helm-def-source--info-files)
          :buffer "*helm Info*"
          :preselect (and default
                          (concat "\\_<" (regexp-quote default) "\\_>")))))

(defun helm-get-info-top-node-items ()
  (require 'info)
  (let ((files (cl-loop for d in (or Info-directory-list
                                     Info-default-directory-list)
                        append (directory-files d nil "\\.info"))))
    (helm-fast-remove-dups
     (cl-loop for f in files collect
              (replace-regexp-in-string
               "\\(\\.info-?[0-9]*\\|\\.info-?[0-9]*\\.gz\\)\\'" "" f))
     :test 'equal)))

(customize-set-variable 'helm-default-info-index-list
                        (helm-get-info-top-node-items))
thierryvolpiatto commented 8 years ago

Of course if merged, configuring helm-default-info-index-list would not be needed, we would use this by default.

thierryvolpiatto commented 8 years ago

And yes we need copy-sequence in candidates unless we use in-buffer method.

thierryvolpiatto commented 8 years ago

Now helm-default-info-index-list is build from info directories. Here the last version of your code slightly modified:

(defvar helm-info-searched (make-ring 32))

(defun helm-info-search-index (candidate)
  "Search the index of CANDIDATE's Info file using helm-info-<CANDIDATE>"
  (let ((helm-info-function
         (intern-soft (concat "helm-info-" candidate))))
    (when (fboundp helm-info-function)
      (funcall helm-info-function)
      (ring-insert helm-info-searched candidate))))

(defun helm-def-source--info-files ()
  (helm-build-sync-source "Helm Info"
    :candidates
    (lambda () (copy-sequence helm-default-info-index-list))
    :candidate-number-limit 999
    :candidate-transformer
    (lambda (candidates)
      (sort candidates #'string-lessp))
    :nomark t
    :action '(("Search index" . helm-info-search-index))))

(defun helm-info ()
  "Preconfigured `helm' for searching Info files indices"
  (interactive)
  (let ((default (unless (ring-empty-p helm-info-searched)
                   (ring-ref helm-info-searched 0))))
    (helm :sources (helm-def-source--info-files)
          :buffer "*helm Info*"
          :preselect (and default
                          (concat "\\_<" (regexp-quote default) "\\_>")))))
xiongtx commented 8 years ago

I didn't add a key binding for helm-info in helm-config.el, but I think helm-command-prefix-key h I would correspond nicely with helm-info-at-point, which is currently helm-command-prefix-key h i.

thierryvolpiatto commented 8 years ago

Tianxiang Xiong notifications@github.com writes:

I didn't add a key binding in helm-config.el, but I think helm-command-prefx h I (capital I, the 9th letter of the English alphabet) would corresponding nicely with helm-info-at-point.

helm-info-at-point is already bound to <prefix>h i and in emacs C-h I is describe-input-method which maybe confusing (for helm-info) but I have not a strong opinion on this.

Thierry