Klimi's new dotfiles with stow.
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

469 строки
19 KiB

4 лет назад
  1. ;;; helm-locate.el --- helm interface for locate. -*- 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. ;; NOTE for WINDOZE users:
  14. ;; You have to install Everything with his command line interface here:
  15. ;; http://www.voidtools.com/download.php
  16. ;;; Code:
  17. (require 'cl-lib)
  18. (require 'helm)
  19. (require 'helm-types)
  20. (require 'helm-help)
  21. (defgroup helm-locate nil
  22. "Locate related Applications and libraries for Helm."
  23. :group 'helm)
  24. (defcustom helm-locate-db-file-regexp "m?locate\.db$"
  25. "Default regexp to match locate database.
  26. If nil Search in all files."
  27. :type 'string
  28. :group 'helm-locate)
  29. (defcustom helm-ff-locate-db-filename "locate.db"
  30. "The basename of the locatedb file you use locally in your directories.
  31. When this is set and `helm' find such a file in the directory from
  32. where you launch locate, it will use this file and will not prompt you
  33. for a db file.
  34. Note that this happen only when locate is launched with a prefix arg."
  35. :group 'helm-locate
  36. :type 'string)
  37. (defcustom helm-locate-command nil
  38. "A list of arguments for locate program.
  39. Helm will calculate a default value for your system on startup unless
  40. `helm-locate-command' is non-nil, here the default values it will use
  41. according to your system:
  42. Gnu/linux: \"locate %s -e -A --regex %s\"
  43. berkeley-unix: \"locate %s %s\"
  44. windows-nt: \"es %s %s\"
  45. Others: \"locate %s %s\"
  46. This string will be passed to format so it should end with `%s'.
  47. The first format spec is used for the \"-i\" value of locate/es,
  48. So don't set it directly but use `helm-locate-case-fold-search'
  49. for this.
  50. The last option must be the one preceding pattern i.e \"-r\" or \"--regex\".
  51. You will be able to pass other options such as \"-b\" or \"l\"
  52. during helm invocation after entering pattern only when multi matching,
  53. not when fuzzy matching.
  54. Note that the \"-b\" option is added automatically by helm when
  55. var `helm-locate-fuzzy-match' is non-nil and switching back from
  56. multimatch to fuzzy matching (this is done automatically when a space
  57. is detected in pattern)."
  58. :type 'string
  59. :group 'helm-locate)
  60. (defcustom helm-locate-create-db-command
  61. "updatedb -l 0 -o '%s' -U '%s'"
  62. "Command used to create a locale locate db file."
  63. :type 'string
  64. :group 'helm-locate)
  65. (defcustom helm-locate-case-fold-search helm-case-fold-search
  66. "It have the same meaning as `helm-case-fold-search'.
  67. The -i option of locate will be used depending of value of
  68. `helm-pattern' when this is set to 'smart.
  69. When nil \"-i\" will not be used at all.
  70. and when non--nil it will always be used.
  71. NOTE: the -i option of the \"es\" command used on windows does
  72. the opposite of \"locate\" command."
  73. :group 'helm-locate
  74. :type 'symbol)
  75. (defcustom helm-locate-fuzzy-match nil
  76. "Enable fuzzy matching in `helm-locate'.
  77. Note that when this is enabled searching is done on basename."
  78. :group 'helm-locate
  79. :type 'boolean)
  80. (defcustom helm-locate-fuzzy-sort-fn
  81. #'helm-locate-default-fuzzy-sort-fn
  82. "Default fuzzy matching sort function for locate."
  83. :group 'helm-locate
  84. :type 'boolean)
  85. (defcustom helm-locate-project-list nil
  86. "A list of directories, your projects.
  87. When set, allow browsing recursively files in all
  88. directories of this list with `helm-projects-find-files'."
  89. :group 'helm-locate
  90. :type '(repeat string))
  91. (defcustom helm-locate-recursive-dirs-command "locate -i -e -A --regex '^%s' '%s.*$'"
  92. "Command used for recursive directories completion in `helm-find-files'.
  93. For Windows and `es' use something like \"es -r ^%s.*%s.*$\"
  94. The two format specs are mandatory.
  95. If for some reasons you can't use locate because your filesystem
  96. doesn't have a data base, you can use find command from findutils but
  97. be aware that it will be much slower, see `helm-find-files' embebded
  98. help for more infos."
  99. :type 'string
  100. :group 'helm-files)
  101. (defvar helm-locate-map
  102. (let ((map (make-sparse-keymap)))
  103. (set-keymap-parent map helm-generic-files-map)
  104. (define-key map (kbd "DEL") 'helm-delete-backward-no-update)
  105. map))
  106. (defface helm-locate-finish
  107. '((t (:foreground "Green")))
  108. "Face used in mode line when locate process is finish."
  109. :group 'helm-locate)
  110. (defun helm-ff-find-locatedb (&optional from-ff)
  111. "Try to find if a local locatedb file is available.
  112. The search is done in `helm-ff-default-directory' or
  113. fall back to `default-directory' if FROM-FF is nil."
  114. (helm-aif (and helm-ff-locate-db-filename
  115. (locate-dominating-file
  116. (or (and from-ff
  117. helm-ff-default-directory)
  118. default-directory)
  119. helm-ff-locate-db-filename))
  120. (expand-file-name helm-ff-locate-db-filename it)))
  121. (defun helm-locate-create-db-default-function (db-name directory)
  122. "Default function used to create a locale locate db file.
  123. Argument DB-NAME name of the db file.
  124. Argument DIRECTORY root of file system subtree to scan."
  125. (format helm-locate-create-db-command
  126. db-name (expand-file-name directory)))
  127. (defvar helm-locate-create-db-function
  128. #'helm-locate-create-db-default-function
  129. "Function used to create a locale locate db file.
  130. It should receive the same arguments as
  131. `helm-locate-create-db-default-function'.")
  132. (defun helm-locate-1 (&optional localdb init from-ff default)
  133. "Generic function to run Locate.
  134. Prefix arg LOCALDB when (4) search and use a local locate db file when it
  135. exists or create it, when (16) force update of existing db file
  136. even if exists.
  137. It have no effect when locate command is 'es'.
  138. INIT is a string to use as initial input in prompt.
  139. See `helm-locate-with-db' and `helm-locate'."
  140. (require 'helm-mode)
  141. (helm-locate-set-command)
  142. (let ((pfn (lambda (candidate)
  143. (if (file-directory-p candidate)
  144. (message "Error: The locate Db should be a file")
  145. (if (= (shell-command
  146. (funcall helm-locate-create-db-function
  147. candidate
  148. helm-ff-default-directory))
  149. 0)
  150. (message "New locatedb file `%s' created" candidate)
  151. (error "Failed to create locatedb file `%s'" candidate)))))
  152. (locdb (and localdb
  153. (not (string-match "^es" helm-locate-command))
  154. (or (and (equal '(4) localdb)
  155. (helm-ff-find-locatedb from-ff))
  156. (helm-read-file-name
  157. "Create Locate Db file: "
  158. :initial-input (expand-file-name "locate.db"
  159. (or helm-ff-default-directory
  160. default-directory))
  161. :preselect helm-locate-db-file-regexp
  162. :test (lambda (x)
  163. (if helm-locate-db-file-regexp
  164. ;; Select only locate db files and directories
  165. ;; to allow navigation.
  166. (or (string-match
  167. helm-locate-db-file-regexp x)
  168. (file-directory-p x))
  169. x)))))))
  170. (when (and locdb (or (equal localdb '(16))
  171. (not (file-exists-p locdb))))
  172. (funcall pfn locdb))
  173. (helm-locate-with-db (and localdb locdb) init default)))
  174. (defun helm-locate-set-command ()
  175. "Setup `helm-locate-command' if not already defined."
  176. (unless helm-locate-command
  177. (setq helm-locate-command
  178. (cl-case system-type
  179. (gnu/linux "locate %s -e -A --regex %s")
  180. (berkeley-unix "locate %s %s")
  181. (windows-nt "es %s %s")
  182. (t "locate %s %s")))))
  183. (defun helm-locate-initial-setup ()
  184. (require 'helm-for-files)
  185. (helm-locate-set-command))
  186. (defvar helm-file-name-history nil)
  187. (defun helm-locate-with-db (&optional db initial-input default)
  188. "Run locate -d DB.
  189. If DB is not given or nil use locate without -d option.
  190. Argument DB can be given as a string or list of db files.
  191. Argument INITIAL-INPUT is a string to use as initial-input.
  192. See also `helm-locate'."
  193. (require 'helm-files)
  194. (when (and db (stringp db)) (setq db (list db)))
  195. (helm-locate-set-command)
  196. (let ((helm-locate-command
  197. (if db
  198. (replace-regexp-in-string
  199. "locate"
  200. (format (if helm-locate-fuzzy-match
  201. "locate -b -d '%s'" "locate -d '%s'")
  202. (mapconcat 'identity
  203. ;; Remove eventually
  204. ;; marked directories by error.
  205. (cl-loop for i in db
  206. unless (file-directory-p i)
  207. ;; expand-file-name to resolve
  208. ;; abbreviated fnames not
  209. ;; expanding inside single
  210. ;; quotes i.e. '%s'.
  211. collect (expand-file-name i))
  212. ":"))
  213. helm-locate-command)
  214. (if (and helm-locate-fuzzy-match
  215. (not (string-match-p "\\`locate -b" helm-locate-command)))
  216. (replace-regexp-in-string
  217. "\\`locate" "locate -b" helm-locate-command)
  218. helm-locate-command))))
  219. (setq helm-file-name-history (mapcar 'helm-basename file-name-history))
  220. (helm :sources 'helm-source-locate
  221. :buffer "*helm locate*"
  222. :ff-transformer-show-only-basename nil
  223. :input initial-input
  224. :default default
  225. :history 'helm-file-name-history)))
  226. (defun helm-locate-update-mode-line (process-name)
  227. "Update mode-line with PROCESS-NAME status information."
  228. (with-helm-window
  229. (setq mode-line-format
  230. `(" " mode-line-buffer-identification " "
  231. (:eval (format "L%s" (helm-candidate-number-at-point))) " "
  232. (:eval (propertize
  233. (format "[%s process finished - (%s results)]"
  234. (max (1- (count-lines
  235. (point-min) (point-max)))
  236. 0)
  237. ,process-name)
  238. 'face 'helm-locate-finish))))
  239. (force-mode-line-update)))
  240. (defun helm-locate-init ()
  241. "Initialize async locate process for `helm-source-locate'."
  242. (let* ((locate-is-es (string-match "\\`es" helm-locate-command))
  243. (real-locate (string-match "\\`locate" helm-locate-command))
  244. (case-sensitive-flag (if locate-is-es "-i" ""))
  245. (ignore-case-flag (if (or locate-is-es
  246. (not real-locate)) "" "-i"))
  247. (args (helm-mm-split-pattern helm-pattern))
  248. (cmd (format helm-locate-command
  249. (cl-case helm-locate-case-fold-search
  250. (smart (let ((case-fold-search nil))
  251. (if (string-match "[[:upper:]]" helm-pattern)
  252. case-sensitive-flag
  253. ignore-case-flag)))
  254. (t (if helm-locate-case-fold-search
  255. ignore-case-flag
  256. case-sensitive-flag)))
  257. (concat
  258. ;; The pattern itself.
  259. (shell-quote-argument (car args)) " "
  260. ;; Possible locate args added
  261. ;; after pattern, don't quote them.
  262. (mapconcat 'identity (cdr args) " "))))
  263. (default-directory (if (file-directory-p default-directory)
  264. default-directory "/")))
  265. (helm-log "Starting helm-locate process")
  266. (helm-log "Command line used was:\n\n%s"
  267. (concat ">>> " (propertize cmd 'face 'font-lock-comment-face) "\n\n"))
  268. (prog1
  269. (start-process-shell-command
  270. "locate-process" helm-buffer
  271. cmd)
  272. (set-process-sentinel
  273. (get-buffer-process helm-buffer)
  274. (lambda (process event)
  275. (let* ((err (process-exit-status process))
  276. (noresult (= err 1)))
  277. (cond (noresult
  278. (with-helm-buffer
  279. (unless (cdr helm-sources)
  280. (insert (concat "* Exit with code 1, no result found,"
  281. " command line was:\n\n "
  282. cmd)))))
  283. ((string= event "finished\n")
  284. (when (and helm-locate-fuzzy-match
  285. (not (string-match-p "\\s-" helm-pattern)))
  286. (helm-redisplay-buffer))
  287. (helm-locate-update-mode-line "Locate"))
  288. (t
  289. (helm-log "Error: Locate %s"
  290. (replace-regexp-in-string "\n" "" event))))))))))
  291. (defun helm-locate-default-fuzzy-sort-fn (candidates)
  292. "Default sort function for files in fuzzy matching.
  293. Sort is done on basename of CANDIDATES."
  294. (helm-fuzzy-matching-default-sort-fn-1 candidates nil t))
  295. (defclass helm-locate-override-inheritor (helm-type-file) ())
  296. (defclass helm-locate-source (helm-source-async helm-locate-override-inheritor)
  297. ((init :initform 'helm-locate-initial-setup)
  298. (candidates-process :initform 'helm-locate-init)
  299. (requires-pattern :initform 3)
  300. (history :initform 'helm-file-name-history)
  301. (persistent-action :initform 'helm-ff-kill-or-find-buffer-fname)
  302. (candidate-number-limit :initform 9999)
  303. (redisplay :initform (progn helm-locate-fuzzy-sort-fn))
  304. (group :initform 'helm-locate)))
  305. ;; Override helm-type-file class keymap.
  306. (defmethod helm--setup-source :after ((source helm-locate-override-inheritor))
  307. (setf (slot-value source 'keymap) helm-locate-map))
  308. (defvar helm-source-locate
  309. (helm-make-source "Locate" 'helm-locate-source
  310. :pattern-transformer 'helm-locate-pattern-transformer
  311. ;; :match-part is only used here to tell helm which part
  312. ;; of candidate to highlight.
  313. :match-part (lambda (candidate)
  314. (if (or (string-match-p " -b\\'" helm-pattern)
  315. (and helm-locate-fuzzy-match
  316. (not (string-match "\\s-" helm-pattern))))
  317. (helm-basename candidate)
  318. candidate))))
  319. (defun helm-locate-pattern-transformer (pattern)
  320. (if helm-locate-fuzzy-match
  321. ;; When fuzzy is enabled helm add "-b" option on startup.
  322. (cond ((string-match-p " " pattern)
  323. (when (string-match "\\`locate -b" helm-locate-command)
  324. (setq helm-locate-command
  325. (replace-match "locate" t t helm-locate-command)))
  326. pattern)
  327. (t
  328. (unless (string-match-p "\\`locate -b" helm-locate-command)
  329. (setq helm-locate-command
  330. (replace-regexp-in-string
  331. "\\`locate" "locate -b" helm-locate-command)))
  332. (helm--mapconcat-pattern pattern)))
  333. pattern))
  334. (defun helm-locate-find-dbs-in-projects (&optional update)
  335. (let* ((pfn (lambda (candidate directory)
  336. (unless (= (shell-command
  337. (funcall helm-locate-create-db-function
  338. candidate
  339. directory))
  340. 0)
  341. (error "Failed to create locatedb file `%s'" candidate)))))
  342. (cl-loop for p in helm-locate-project-list
  343. for db = (expand-file-name
  344. helm-ff-locate-db-filename
  345. (file-name-as-directory p))
  346. if (and (null update) (file-exists-p db))
  347. collect db
  348. else do (funcall pfn db p)
  349. and collect db)))
  350. ;;; Directory completion for hff.
  351. ;;
  352. (defclass helm-locate-subdirs-source (helm-source-in-buffer)
  353. ((basedir :initarg :basedir
  354. :initform nil
  355. :custom string)
  356. (subdir :initarg :subdir
  357. :initform nil
  358. :custom 'string)
  359. (data :initform #'helm-locate-init-subdirs)
  360. (group :initform 'helm-locate)))
  361. (defun helm-locate-init-subdirs ()
  362. (with-temp-buffer
  363. (call-process-shell-command
  364. (format helm-locate-recursive-dirs-command
  365. (if (string-match-p "\\`es" helm-locate-recursive-dirs-command)
  366. ;; Fix W32 paths.
  367. (replace-regexp-in-string
  368. "/" "\\\\\\\\" (helm-attr 'basedir))
  369. (helm-attr 'basedir))
  370. (helm-attr 'subdir))
  371. nil t nil)
  372. (buffer-string)))
  373. ;;;###autoload
  374. (defun helm-projects-find-files (update)
  375. "Find files with locate in `helm-locate-project-list'.
  376. With a prefix arg refresh the database in each project."
  377. (interactive "P")
  378. (helm-locate-set-command)
  379. (cl-assert (and (string-match-p "\\`locate" helm-locate-command)
  380. (executable-find "updatedb"))
  381. nil "Unsupported locate version")
  382. (let ((dbs (helm-locate-find-dbs-in-projects update)))
  383. (if dbs
  384. (helm-locate-with-db dbs)
  385. (user-error "No projects found, please setup `helm-locate-project-list'"))))
  386. ;;;###autoload
  387. (defun helm-locate (arg)
  388. "Preconfigured `helm' for Locate.
  389. Note: you can add locate options after entering pattern.
  390. See 'man locate' for valid options and also `helm-locate-command'.
  391. You can specify a local database with prefix argument ARG.
  392. With two prefix arg, refresh the current local db or create it
  393. if it doesn't exists.
  394. To create a user specific db, use
  395. \"updatedb -l 0 -o db_path -U directory\".
  396. Where db_path is a filename matched by
  397. `helm-locate-db-file-regexp'."
  398. (interactive "P")
  399. (helm-set-local-variable 'helm-async-outer-limit-hook
  400. (list (lambda ()
  401. (when (and helm-locate-fuzzy-match
  402. (not (string-match-p
  403. "\\s-" helm-pattern)))
  404. (helm-redisplay-buffer)))))
  405. (setq helm-ff-default-directory default-directory)
  406. (helm-locate-1 arg nil nil (thing-at-point 'filename)))
  407. (provide 'helm-locate)
  408. ;; Local Variables:
  409. ;; byte-compile-warnings: (not obsolete)
  410. ;; coding: utf-8
  411. ;; indent-tabs-mode: nil
  412. ;; End:
  413. ;;; helm-locate.el ends here