Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

268 lines
9.8 KiB

пре 5 година
  1. ;;; helm-info.el --- Browse info index with helm -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2012 ~ 2019 Thierry Volpiatto <thierry.volpiatto@gmail.com>
  3. ;; This program is free software; you can redistribute it and/or modify
  4. ;; it under the terms of the GNU General Public License as published by
  5. ;; the Free Software Foundation, either version 3 of the License, or
  6. ;; (at your option) any later version.
  7. ;; This program is distributed in the hope that it will be useful,
  8. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. ;; GNU General Public License for more details.
  11. ;; You should have received a copy of the GNU General Public License
  12. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ;;; Code:
  14. (require 'cl-lib)
  15. (require 'helm)
  16. (require 'helm-lib)
  17. (require 'helm-utils)
  18. (require 'info)
  19. (declare-function Info-index-nodes "info" (&optional file))
  20. (declare-function Info-goto-node "info" (&optional fork))
  21. (declare-function Info-find-node "info.el" (filename nodename &optional no-going-back))
  22. (defvar Info-history)
  23. (defvar Info-directory-list)
  24. ;;; Customize
  25. (defgroup helm-info nil
  26. "Info-related applications and libraries for Helm."
  27. :group 'helm)
  28. (defcustom helm-info-default-sources
  29. '(helm-source-info-elisp
  30. helm-source-info-cl
  31. helm-source-info-eieio
  32. helm-source-info-pages)
  33. "Default sources to use for looking up symbols at point in Info
  34. files with `helm-info-at-point'."
  35. :group 'helm-info
  36. :type '(repeat (choice symbol)))
  37. ;;; Build info-index sources with `helm-info-source' class.
  38. (cl-defun helm-info-init (&optional (file (helm-attr 'info-file)))
  39. ;; Allow reinit candidate buffer when using edebug.
  40. (helm-aif (and debug-on-error
  41. (helm-candidate-buffer))
  42. (kill-buffer it))
  43. (unless (helm-candidate-buffer)
  44. (save-selected-window
  45. (info file " *helm info temp buffer*")
  46. (let ((tobuf (helm-candidate-buffer 'global))
  47. Info-history
  48. start end line)
  49. (cl-dolist (node (Info-index-nodes))
  50. (Info-goto-node node)
  51. (goto-char (point-min))
  52. (while (search-forward "\n* " nil t)
  53. (unless (search-forward "Menu:\n" (1+ (point-at-eol)) t)
  54. (setq start (point-at-bol)
  55. ;; Fix issue #1503 by getting the invisible
  56. ;; info displayed on next line in long strings.
  57. ;; e.g "* Foo.\n (line 12)" instead of
  58. ;; "* Foo.(line 12)"
  59. end (or (save-excursion
  60. (goto-char (point-at-bol))
  61. (re-search-forward "(line +[0-9]+)" nil t))
  62. (point-at-eol))
  63. ;; Long string have a new line inserted before the
  64. ;; invisible spec, remove it.
  65. line (replace-regexp-in-string
  66. "\n" "" (buffer-substring start end)))
  67. (with-current-buffer tobuf
  68. (insert line)
  69. (insert "\n")))))
  70. (bury-buffer)))))
  71. (defun helm-info-goto (node-line)
  72. (Info-goto-node (car node-line))
  73. (helm-goto-line (cdr node-line)))
  74. (defun helm-info-display-to-real (line)
  75. (and (string-match
  76. ;; This regexp is stolen from Info-apropos-matches
  77. "\\* +\\([^\n]*.+[^\n]*\\):[ \t]+\\([^\n]*\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?" line)
  78. (cons (format "(%s)%s" (helm-attr 'info-file) (match-string 2 line))
  79. (string-to-number (or (match-string 3 line) "1")))))
  80. (defclass helm-info-source (helm-source-in-buffer)
  81. ((info-file :initarg :info-file
  82. :initform nil
  83. :custom 'string)
  84. (init :initform #'helm-info-init)
  85. (display-to-real :initform #'helm-info-display-to-real)
  86. (get-line :initform #'buffer-substring)
  87. (action :initform '(("Goto node" . helm-info-goto)))))
  88. (defmacro helm-build-info-source (fname &rest args)
  89. `(helm-make-source (concat "Info Index: " ,fname) 'helm-info-source
  90. :info-file ,fname ,@args))
  91. (defun helm-build-info-index-command (name doc source buffer)
  92. "Define a helm command NAME with documentation DOC.
  93. Arg SOURCE will be an existing helm source named
  94. `helm-source-info-<NAME>' and BUFFER a string buffer name."
  95. (defalias (intern (concat "helm-info-" name))
  96. (lambda ()
  97. (interactive)
  98. (helm :sources source
  99. :buffer buffer
  100. :candidate-number-limit 1000))
  101. doc))
  102. (defun helm-define-info-index-sources (var-value &optional commands)
  103. "Define helm sources named helm-source-info-<NAME>.
  104. Sources are generated for all entries of `helm-default-info-index-list'.
  105. If COMMANDS arg is non-nil, also build commands named `helm-info-<NAME>'.
  106. Where NAME is an element of `helm-default-info-index-list'."
  107. (cl-loop for str in var-value
  108. for sym = (intern (concat "helm-source-info-" str))
  109. do (set sym (helm-build-info-source str))
  110. when commands
  111. do (helm-build-info-index-command
  112. str (format "Predefined helm for %s info." str)
  113. sym (format "*helm info %s*" str))))
  114. (defun helm-info-index-set (var value)
  115. (set var value)
  116. (helm-define-info-index-sources value t))
  117. ;;; Search Info files
  118. ;; `helm-info' is the main entry point here. It prompts the user for an Info
  119. ;; file, then a term in the file's index to jump to.
  120. (defvar helm-info-searched (make-ring 32)
  121. "Ring of previously searched Info files.")
  122. (defun helm-get-info-files ()
  123. "Return list of Info files to use for `helm-info'.
  124. Elements of the list are strings of Info file names without
  125. extensions (e.g. \"emacs\" for file \"emacs.info.gz\"). Info
  126. files are found by searching directories in
  127. `Info-directory-list'."
  128. (info-initialize) ; Build Info-directory-list from INFOPATH (Issue #2118)
  129. (let ((files (cl-loop for d in (or Info-directory-list
  130. Info-default-directory-list)
  131. when (file-directory-p d)
  132. append (directory-files d nil "\\.info"))))
  133. (helm-fast-remove-dups
  134. (cl-loop for f in files collect
  135. (helm-file-name-sans-extension f))
  136. :test 'equal)))
  137. (defcustom helm-default-info-index-list
  138. (helm-get-info-files)
  139. "Info files to search in with `helm-info'."
  140. :group 'helm-info
  141. :type '(repeat (choice string))
  142. :set 'helm-info-index-set)
  143. (defun helm-info-search-index (candidate)
  144. "Search the index of CANDIDATE's Info file using the function
  145. helm-info-<CANDIDATE>."
  146. (let ((helm-info-function
  147. (intern-soft (concat "helm-info-" candidate))))
  148. (when (fboundp helm-info-function)
  149. (funcall helm-info-function)
  150. (ring-insert helm-info-searched candidate))))
  151. (defun helm-def-source--info-files ()
  152. "Return a `helm' source for Info files."
  153. (helm-build-sync-source "Helm Info"
  154. :candidates
  155. (lambda () (copy-sequence helm-default-info-index-list))
  156. :candidate-number-limit 999
  157. :candidate-transformer
  158. (lambda (candidates)
  159. (sort candidates #'string-lessp))
  160. :nomark t
  161. :action '(("Search index" . helm-info-search-index))))
  162. ;;;###autoload
  163. (defun helm-info (&optional refresh)
  164. "Preconfigured `helm' for searching Info files' indices.
  165. With a prefix argument \\[universal-argument], set REFRESH to non-nil.
  166. Optional parameter REFRESH, when non-nil, reevaluates
  167. `helm-default-info-index-list'. If the variable has been
  168. customized, set it to its saved value. If not, set it to its
  169. standard value. See `custom-reevaluate-setting' for more.
  170. REFRESH is useful when new Info files are installed. If
  171. `helm-default-info-index-list' has not been customized, the new
  172. Info files are made available."
  173. (interactive "P")
  174. (let ((default (unless (ring-empty-p helm-info-searched)
  175. (ring-ref helm-info-searched 0))))
  176. (when refresh
  177. (custom-reevaluate-setting 'helm-default-info-index-list))
  178. (helm :sources (helm-def-source--info-files)
  179. :buffer "*helm Info*"
  180. :preselect (and default
  181. (concat "\\_<" (regexp-quote default) "\\_>")))))
  182. ;;;; Info at point
  183. ;; `helm-info-at-point' is the main entry point here. It searches for the
  184. ;; symbol at point through the Info sources defined in
  185. ;; `helm-info-default-sources' and jumps to it.
  186. (defvar helm-info--pages-cache nil
  187. "Cache for all Info pages on the system.")
  188. (defvar helm-source-info-pages
  189. (helm-build-sync-source "Info Pages"
  190. :init #'helm-info-pages-init
  191. :candidates (lambda () helm-info--pages-cache)
  192. :action '(("Show with Info" .
  193. (lambda (node-str)
  194. (info (replace-regexp-in-string
  195. "^[^:]+: " "" node-str)))))
  196. :requires-pattern 2)
  197. "Helm source for Info pages.")
  198. (defun helm-info-pages-init ()
  199. "Collect candidates for initial Info node Top."
  200. (or helm-info--pages-cache
  201. (let ((info-topic-regexp "\\* +\\([^:]+: ([^)]+)[^.]*\\)\\."))
  202. (save-selected-window
  203. (info "dir" " *helm info temp buffer*")
  204. (Info-find-node "dir" "top")
  205. (goto-char (point-min))
  206. (while (re-search-forward info-topic-regexp nil t)
  207. (push (match-string-no-properties 1)
  208. helm-info--pages-cache))
  209. (kill-buffer)))))
  210. ;;;###autoload
  211. (defun helm-info-at-point ()
  212. "Preconfigured `helm' for searching info at point."
  213. (interactive)
  214. (cl-loop for src in helm-info-default-sources
  215. for name = (if (symbolp src)
  216. (assoc 'name (symbol-value src))
  217. (assoc 'name src))
  218. unless name
  219. do (warn "Couldn't build source `%S' without its info file" src))
  220. (helm :sources helm-info-default-sources
  221. :buffer "*helm info*"))
  222. (provide 'helm-info)
  223. ;; Local Variables:
  224. ;; byte-compile-warnings: (not obsolete)
  225. ;; coding: utf-8
  226. ;; indent-tabs-mode: nil
  227. ;; End:
  228. ;;; helm-info.el ends here