summaryrefslogtreecommitdiff
path: root/helm-info.el
blob: ef6b159dd3a3e87512e43279d662682b4dc51323 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
;;; helm-info.el --- Browse info index with helm -*- lexical-binding: t -*-

;; Copyright (C) 2012 ~ 2014 Thierry Volpiatto <thierry.volpiatto@gmail.com>

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

;;; Code:

(require 'cl-lib)
(require 'helm)
(require 'helm-plugin)

(declare-function Info-index-nodes "info" (&optional file))
(declare-function Info-goto-node "info" (&optional fork))
(declare-function Info-find-node "info.el" (filename nodename &optional no-going-back))
(defvar Info-history)


(defgroup helm-info nil
  "Info related Applications and libraries for Helm."
  :group 'helm)

;;; Build info-index sources with `helm-info-source' class.
;;
;;
(cl-defun helm-info-init (&optional (file (helm-attr 'info-file)))
  ;; Allow reinit candidate buffer when using edebug.
  (helm-aif (and debug-on-error
                 (helm-candidate-buffer))
      (kill-buffer it))
  (unless (helm-candidate-buffer)
    (save-window-excursion
      (info file)
      (let ((tobuf (helm-candidate-buffer 'global))
            (infobuf (current-buffer))
            Info-history
            start end)
        (cl-dolist (node (Info-index-nodes))
          (Info-goto-node node)
          (goto-char (point-min))
          (while (search-forward "\n* " nil t)
            (unless (search-forward "Menu:\n" (1+ (point-at-eol)) t)
              (setq start (point-at-bol)
                    end (point-at-eol))
              (with-current-buffer tobuf
                (insert-buffer-substring infobuf start end)
                (insert "\n")))))))))

(defun helm-info-goto (node-line)
  (Info-goto-node (car node-line))
  (helm-goto-line (cdr node-line)))

(defun helm-info-display-to-real (line)
  (and (string-match
        ;; This regexp is stolen from Info-apropos-matches
        "\\* +\\([^\n]*.+[^\n]*\\):[ \t]+\\([^\n]*\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?" line)
       (cons (format "(%s)%s" (helm-attr 'info-file) (match-string 2 line))
             (string-to-number (or (match-string 3 line) "1")))))

(defclass helm-info-source (helm-source-in-buffer)
  ((info-file :initarg :info-file
              :initform nil
              :custom 'string)
   (init :initform #'helm-info-init)
   (display-to-real :initform #'helm-info-display-to-real)
   (get-line :initform #'buffer-substring)
   (action :initform '(("Goto node" . helm-info-goto)))))

(defmacro helm-build-info-source (fname &rest args)
  `(helm-make-source (concat "Info Index: " ,fname) 'helm-info-source
     :info-file ,fname ,@args))

(defun helm-build-info-index-command (name doc source buffer)
  "Define an helm command NAME with documentation DOC.
Arg SOURCE will be an existing helm source named
`helm-source-info-<NAME>' and BUFFER a string buffer name."
  (eval (list 'defun name nil doc
              (list 'interactive)
              (list 'helm
                    :sources source
                    :buffer buffer
                    :candidate-number-limit 1000))))

(defun helm-define-info-index-sources (var-value &optional commands)
  "Define helm sources named helm-source-info-<NAME>.
Sources are generated for all entries of `helm-default-info-index-list'.
If COMMANDS arg is non--nil build also commands named `helm-info<NAME>'.
Where NAME is one of `helm-default-info-index-list'."
  (cl-loop with symbols = (cl-loop for str in var-value
                                collect
                                (intern (concat "helm-source-info-" str)))
        for sym in symbols
        for str in var-value
        do (set sym (helm-build-info-source str))
        when commands
        do (let ((com (intern (concat "helm-info-" str))))
             (helm-build-info-index-command
              com (format "Predefined helm for %s info." str)
              sym (format "*helm info %s*" str)))))

(defun helm-info-index-set (var value)
  (set var value)
  (helm-define-info-index-sources value t))

(defcustom helm-default-info-index-list
  '("elisp" "cl" "org" "gnus" "tramp" "ratpoison"
    "zsh" "bash" "coreutils" "fileutils"
    "find" "sh-utils" "textutils" "libc"
    "make" "automake" "autoconf" "eintr"
    "emacs" "elib" "eieio" "gauche-refe" "guile"
    "guile-tut" "goops" "screen" "latex" "gawk"
    "sed" "m4" "wget" "binutils" "as" "bfd" "gprof"
    "ld" "diff" "flex" "grep" "gzip" "libtool"
    "texinfo" "info" "gdb" "stabs" "cvsbook" "cvs"
    "bison" "id-utils" "global")
  "Info Manual entries to use for building helm info index commands."
  :group 'helm-info
  :type  '(repeat (choice string))
  :set   'helm-info-index-set)

(defcustom helm-info-default-sources
  '(helm-source-info-elisp
    helm-source-info-cl
    helm-source-info-eieio
    helm-source-info-pages)
  "The default sources to use in `helm-info-at-point'."
  :group 'helm-info
  :type '(repeat (choice symbol)))


;;; Info pages
(defvar helm-info--pages-cache nil
  "Cache for all info pages on system.")

(defun helm-info-pages-init ()
  "Collect candidates for initial Info node Top."
  (if helm-info--pages-cache
      helm-info--pages-cache
    (let ((info-topic-regexp "\\* +\\([^:]+: ([^)]+)[^.]*\\)\\.")
          topics)
      (require 'info)
      (with-temp-buffer
        (Info-find-node "dir" "top")
        (goto-char (point-min))
        (while (re-search-forward info-topic-regexp nil t)
          (push (match-string-no-properties 1) topics))
        (kill-buffer))
      (setq helm-info--pages-cache topics))))

(defvar helm-source-info-pages
  (helm-build-sync-source "Info Pages"
    :init #'helm-info-pages-init
    :candidates (lambda () helm-info--pages-cache)
    :action '(("Show with Info" .(lambda (node-str)
                                  (info (replace-regexp-in-string
                                         "^[^:]+: " "" node-str)))))
    :requires-pattern 2))

;;;###autoload
(defun helm-info-at-point ()
  "Preconfigured `helm' for searching info at point.
With a prefix-arg insert symbol at point."
  (interactive)
  (helm :sources helm-info-default-sources
        :buffer "*helm info*"))

(provide 'helm-info)

;; Local Variables:
;; byte-compile-warnings: (not cl-functions obsolete)
;; coding: utf-8
;; indent-tabs-mode: nil
;; End:

;;; helm-info.el ends here