Klimi's new dotfiles with stow.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

377 行
15 KiB

  1. ;;; helm-imenu.el --- Helm interface for Imenu -*- 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 'imenu)
  18. (require 'helm-utils)
  19. (require 'helm-help)
  20. (declare-function which-function "which-func")
  21. (defgroup helm-imenu nil
  22. "Imenu related libraries and applications for helm."
  23. :group 'helm)
  24. (defcustom helm-imenu-delimiter " / "
  25. "Delimit types of candidates and his value in `helm-buffer'."
  26. :group 'helm-imenu
  27. :type 'string)
  28. (defcustom helm-imenu-execute-action-at-once-if-one
  29. #'helm-imenu--execute-action-at-once-p
  30. "Goto the candidate when only one is remaining."
  31. :group 'helm-imenu
  32. :type 'function)
  33. (defcustom helm-imenu-all-buffer-assoc nil
  34. "Major mode association alist for `helm-imenu-in-all-buffers'.
  35. Allow `helm-imenu-in-all-buffers' searching in these associated buffers
  36. even if they are not derived from each other.
  37. The alist is bidirectional, i.e no need to add '((foo . bar) (bar . foo))
  38. only '((foo . bar)) is needed."
  39. :type '(alist :key-type symbol :value-type symbol)
  40. :group 'helm-imenu)
  41. (defcustom helm-imenu-in-all-buffers-separate-sources t
  42. "Display imenu index of each buffer in its own source when non-nil.
  43. When nil all candidates are displayed in a single source.
  44. NOTE: Each source will have as name \"Imenu <buffer-name>\".
  45. `helm-source-imenu-all' will not be set, however it will continue
  46. to be used as a flag for using default as input, if you do not want
  47. this behavior, remove it from `helm-sources-using-default-as-input'
  48. even if not using a single source to display imenu in all buffers."
  49. :type 'boolean
  50. :group 'helm-imenu)
  51. (defcustom helm-imenu-type-faces
  52. '(("^Variables$" . font-lock-variable-name-face)
  53. ("^\\(Function\\|Functions\\|Defuns\\)$" . font-lock-function-name-face)
  54. ("^\\(Types\\|Provides\\|Requires\\|Classes\\|Class\\|Includes\\|Imports\\|Misc\\|Code\\)$" . font-lock-type-face))
  55. "Faces for showing type in helm-imenu.
  56. This is a list of cons cells. The cdr of each cell is a face to be used,
  57. and it can also just be like \\='(:foreground \"yellow\").
  58. Each car is a regexp match pattern of the imenu type string."
  59. :group 'helm-faces
  60. :type '(repeat
  61. (cons
  62. (regexp :tag "Imenu type regexp pattern")
  63. (sexp :tag "Face"))))
  64. (defcustom helm-imenu-extra-modes nil
  65. "Extra modes where helm-imenu-in-all-buffers should look into."
  66. :group 'helm-imenu
  67. :type '(repeat symbol))
  68. ;;; keymap
  69. (defvar helm-imenu-map
  70. (let ((map (make-sparse-keymap)))
  71. (set-keymap-parent map helm-map)
  72. (define-key map (kbd "M-<down>") 'helm-imenu-next-section)
  73. (define-key map (kbd "M-<up>") 'helm-imenu-previous-section)
  74. map))
  75. (defcustom helm-imenu-lynx-style-map nil
  76. "Use Arrow keys to jump to occurences."
  77. :group 'helm-imenu
  78. :type 'boolean
  79. :set (lambda (var val)
  80. (set var val)
  81. (if val
  82. (progn
  83. (define-key helm-imenu-map (kbd "<right>") 'helm-execute-persistent-action)
  84. (define-key helm-imenu-map (kbd "<left>") 'helm-maybe-exit-minibuffer))
  85. (define-key helm-imenu-map (kbd "<right>") nil)
  86. (define-key helm-imenu-map (kbd "<left>") nil))))
  87. (defun helm-imenu-next-or-previous-section (n)
  88. (with-helm-window
  89. (let* ((fn (lambda ()
  90. (car (split-string
  91. (buffer-substring
  92. (point-at-bol) (point-at-eol))
  93. helm-imenu-delimiter))))
  94. (curtype (funcall fn))
  95. (stop-fn (if (> n 0)
  96. #'helm-end-of-source-p
  97. #'helm-beginning-of-source-p)))
  98. (while (and (not (funcall stop-fn))
  99. (string= curtype (funcall fn)))
  100. (forward-line n))
  101. (helm-mark-current-line)
  102. (helm-follow-execute-persistent-action-maybe))))
  103. (defun helm-imenu-next-section ()
  104. (interactive)
  105. (helm-imenu-next-or-previous-section 1))
  106. (defun helm-imenu-previous-section ()
  107. (interactive)
  108. (helm-imenu-next-or-previous-section -1))
  109. ;;; Internals
  110. (defvar helm-cached-imenu-alist nil)
  111. (make-variable-buffer-local 'helm-cached-imenu-alist)
  112. (defvar helm-cached-imenu-candidates nil)
  113. (make-variable-buffer-local 'helm-cached-imenu-candidates)
  114. (defvar helm-cached-imenu-tick nil)
  115. (make-variable-buffer-local 'helm-cached-imenu-tick)
  116. (defvar helm-imenu--in-all-buffers-cache nil)
  117. (defvar helm-source-imenu nil "See (info \"(emacs)Imenu\")")
  118. (defvar helm-source-imenu-all nil)
  119. (defclass helm-imenu-source (helm-source-sync)
  120. ((candidates :initform 'helm-imenu-candidates)
  121. (candidate-transformer :initform 'helm-imenu-transformer)
  122. (persistent-action :initform 'helm-imenu-persistent-action)
  123. (persistent-help :initform "Show this entry")
  124. (nomark :initform t)
  125. (keymap :initform helm-imenu-map)
  126. (help-message :initform 'helm-imenu-help-message)
  127. (action :initform 'helm-imenu-action)
  128. (group :initform 'helm-imenu)))
  129. (defcustom helm-imenu-fuzzy-match nil
  130. "Enable fuzzy matching in `helm-source-imenu'."
  131. :group 'helm-imenu
  132. :type 'boolean
  133. :set (lambda (var val)
  134. (set var val)
  135. (setq helm-source-imenu
  136. (helm-make-source "Imenu" 'helm-imenu-source
  137. :fuzzy-match helm-imenu-fuzzy-match))))
  138. (defun helm-imenu--maybe-switch-to-buffer (candidate)
  139. (let ((cand (cdr candidate)))
  140. (helm-aif (and (markerp cand) (marker-buffer cand))
  141. (switch-to-buffer it))))
  142. (defun helm-imenu--execute-action-at-once-p ()
  143. (let ((cur (helm-get-selection))
  144. (mb (with-helm-current-buffer
  145. (save-excursion
  146. (goto-char (point-at-bol))
  147. (point-marker)))))
  148. (if (equal (cdr cur) mb)
  149. (prog1 nil
  150. (helm-set-pattern "")
  151. (helm-force-update))
  152. t)))
  153. (defun helm-imenu-action (candidate)
  154. "Default action for `helm-source-imenu'."
  155. (helm-log-run-hook 'helm-goto-line-before-hook)
  156. (helm-imenu--maybe-switch-to-buffer candidate)
  157. (imenu candidate)
  158. ;; If semantic is supported in this buffer
  159. ;; imenu used `semantic-imenu-goto-function'
  160. ;; and position have been highlighted,
  161. ;; no need to highlight again.
  162. (unless (eq imenu-default-goto-function
  163. 'semantic-imenu-goto-function)
  164. (helm-highlight-current-line)))
  165. (defun helm-imenu-persistent-action (candidate)
  166. "Default persistent action for `helm-source-imenu'."
  167. (helm-imenu--maybe-switch-to-buffer candidate)
  168. (imenu candidate)
  169. (helm-highlight-current-line))
  170. (defun helm-imenu-candidates (&optional buffer)
  171. (with-current-buffer (or buffer helm-current-buffer)
  172. (let ((tick (buffer-modified-tick)))
  173. (if (eq helm-cached-imenu-tick tick)
  174. helm-cached-imenu-candidates
  175. (setq imenu--index-alist nil)
  176. (prog1 (setq helm-cached-imenu-candidates
  177. (let ((index (imenu--make-index-alist t)))
  178. (helm-imenu--candidates-1
  179. (delete (assoc "*Rescan*" index) index))))
  180. (setq helm-cached-imenu-tick tick))))))
  181. (defun helm-imenu-candidates-in-all-buffers (&optional build-sources)
  182. (let* ((lst (buffer-list))
  183. (progress-reporter (make-progress-reporter
  184. "Imenu indexing buffers..." 1 (length lst))))
  185. (prog1
  186. (cl-loop with cur-buf = (if build-sources
  187. (current-buffer) helm-current-buffer)
  188. for b in lst
  189. for count from 1
  190. when (with-current-buffer b
  191. (and (or (member major-mode helm-imenu-extra-modes)
  192. (derived-mode-p 'prog-mode))
  193. (helm-same-major-mode-p
  194. cur-buf helm-imenu-all-buffer-assoc)))
  195. if build-sources
  196. collect (helm-make-source
  197. (format "Imenu in %s" (buffer-name b))
  198. 'helm-imenu-source
  199. :candidates (with-current-buffer b
  200. (helm-imenu-candidates b))
  201. :fuzzy-match helm-imenu-fuzzy-match)
  202. else
  203. append (with-current-buffer b
  204. (helm-imenu-candidates b))
  205. do (progress-reporter-update progress-reporter count))
  206. (progress-reporter-done progress-reporter))))
  207. (defun helm-imenu--candidates-1 (alist)
  208. (cl-loop for elm in alist
  209. nconc (cond
  210. ((imenu--subalist-p elm)
  211. (helm-imenu--candidates-1
  212. (cl-loop for (e . v) in (cdr elm) collect
  213. (cons (propertize
  214. e 'helm-imenu-type (car elm))
  215. ;; If value is an integer, convert it
  216. ;; to a marker, otherwise it is a cons cell
  217. ;; and it will be converted on next recursions.
  218. ;; (Issue #1060) [1].
  219. (if (integerp v) (copy-marker v) v)))))
  220. ((listp (cdr elm))
  221. (and elm (list elm)))
  222. (t
  223. ;; bug in imenu, should not be needed.
  224. (and (cdr elm)
  225. ;; Semantic uses overlays whereas imenu uses
  226. ;; markers (issue #1706).
  227. (setcdr elm (pcase (cdr elm) ; Same as [1].
  228. ((and ov (pred overlayp))
  229. (copy-overlay ov))
  230. ((and mk (or (pred markerp)
  231. (pred integerp)))
  232. (copy-marker mk))))
  233. (list elm))))))
  234. (defun helm-imenu--get-prop (item)
  235. ;; property value of ITEM can have itself
  236. ;; a property value which have itself a property value
  237. ;; ...and so on; Return a list of all these
  238. ;; properties values starting at ITEM.
  239. (let* ((prop (get-text-property 0 'helm-imenu-type item))
  240. (lst (list prop item)))
  241. (when prop
  242. (while prop
  243. (setq prop (get-text-property 0 'helm-imenu-type prop))
  244. (and prop (push prop lst)))
  245. lst)))
  246. (defun helm-imenu-transformer (candidates)
  247. (cl-loop for (k . v) in candidates
  248. ;; (k . v) == (symbol-name . marker)
  249. for bufname = (buffer-name
  250. (pcase v
  251. ((pred overlayp) (overlay-buffer v))
  252. ((or (pred markerp) (pred integerp))
  253. (marker-buffer v))))
  254. for types = (or (helm-imenu--get-prop k)
  255. (list (if (with-current-buffer bufname
  256. (derived-mode-p 'prog-mode))
  257. "Function"
  258. "Top level")
  259. k))
  260. for disp1 = (mapconcat
  261. (lambda (x)
  262. (propertize
  263. x 'face
  264. (cl-loop for (p . f) in helm-imenu-type-faces
  265. when (string-match p x) return f
  266. finally return 'default)))
  267. types helm-imenu-delimiter)
  268. for disp = (propertize disp1 'help-echo bufname 'types types)
  269. collect
  270. (cons disp (cons k v))))
  271. ;;;###autoload
  272. (defun helm-imenu ()
  273. "Preconfigured `helm' for `imenu'."
  274. (interactive)
  275. (require 'which-func)
  276. (unless helm-source-imenu
  277. (setq helm-source-imenu
  278. (helm-make-source "Imenu" 'helm-imenu-source
  279. :fuzzy-match helm-imenu-fuzzy-match)))
  280. (let* ((imenu-auto-rescan t)
  281. (str (thing-at-point 'symbol))
  282. (init-reg (and str (concat "\\_<" (regexp-quote str) "\\_>")))
  283. (helm-execute-action-at-once-if-one
  284. helm-imenu-execute-action-at-once-if-one))
  285. (helm :sources 'helm-source-imenu
  286. :default (and str (list init-reg str))
  287. :preselect (helm-aif (which-function)
  288. (concat "\\_<" (regexp-quote it) "\\_>")
  289. init-reg)
  290. :buffer "*helm imenu*")))
  291. ;;;###autoload
  292. (defun helm-imenu-in-all-buffers ()
  293. "Preconfigured helm for fetching imenu entries in all buffers with similar mode as current.
  294. A mode is similar as current if it is the same, it is derived i.e `derived-mode-p'
  295. or it have an association in `helm-imenu-all-buffer-assoc'."
  296. (interactive)
  297. (require 'which-func)
  298. (unless helm-imenu-in-all-buffers-separate-sources
  299. (unless helm-source-imenu-all
  300. (setq helm-source-imenu-all
  301. (helm-make-source "Imenu in all buffers" 'helm-imenu-source
  302. :init (lambda ()
  303. ;; Use a cache to avoid repeatedly sending
  304. ;; progress-reporter message when updating
  305. ;; (Issue #1704).
  306. (setq helm-imenu--in-all-buffers-cache
  307. (helm-imenu-candidates-in-all-buffers)))
  308. :candidates 'helm-imenu--in-all-buffers-cache
  309. :fuzzy-match helm-imenu-fuzzy-match))))
  310. (let* ((imenu-auto-rescan t)
  311. (str (thing-at-point 'symbol))
  312. (init-reg (and str (concat "\\_<" (regexp-quote str) "\\_>")))
  313. (helm-execute-action-at-once-if-one
  314. helm-imenu-execute-action-at-once-if-one)
  315. (helm--maybe-use-default-as-input
  316. (not (null (memq 'helm-source-imenu-all
  317. helm-sources-using-default-as-input))))
  318. (sources (if helm-imenu-in-all-buffers-separate-sources
  319. (helm-imenu-candidates-in-all-buffers 'build-sources)
  320. '(helm-source-imenu-all))))
  321. (helm :sources sources
  322. :default (and str (list init-reg str))
  323. :preselect (helm-aif (which-function)
  324. (concat "\\_<" (regexp-quote it) "\\_>")
  325. init-reg)
  326. :buffer "*helm imenu all*")))
  327. (provide 'helm-imenu)
  328. ;; Local Variables:
  329. ;; byte-compile-warnings: (not obsolete)
  330. ;; coding: utf-8
  331. ;; indent-tabs-mode: nil
  332. ;; End:
  333. ;;; helm-imenu.el ends here