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.

1543 lines
59 KiB

4 years ago
  1. ;;; helm-lib.el --- Helm routines. -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2015 ~ 2019 Thierry Volpiatto <thierry.volpiatto@gmail.com>
  3. ;; Author: Thierry Volpiatto <thierry.volpiatto@gmail.com>
  4. ;; URL: http://github.com/emacs-helm/helm
  5. ;; This program is free software; you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;; This program is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;; All helm functions that don't require specific helm code should go here.
  17. ;;; Code:
  18. (require 'cl-lib)
  19. (declare-function wdired-change-to-dired-mode "wdired.el")
  20. (declare-function wdired-do-symlink-changes "wdired.el")
  21. (declare-function wdired-do-perm-changes "wdired.el")
  22. (declare-function wdired-get-filename "wdired.el")
  23. (declare-function wdired-do-renames "wdired.el")
  24. (declare-function wdired-flag-for-deletion "wdired.el")
  25. (declare-function wdired-normalize-filename "wdired.el")
  26. (declare-function dired-mark-remembered "dired.el")
  27. (declare-function dired-log-summary "dired.el")
  28. (declare-function dired-current-directory "dired.el")
  29. (declare-function ansi-color--find-face "ansi-color.el")
  30. (declare-function ansi-color-apply-sequence "ansi-color.el")
  31. (declare-function helm-get-sources "helm.el")
  32. (declare-function helm-marked-candidates "helm.el")
  33. (declare-function helm-follow-mode-p "helm.el")
  34. (declare-function helm-attr "helm.el")
  35. (declare-function helm-attrset "helm.el")
  36. (declare-function org-open-at-point "org.el")
  37. (declare-function org-content "org.el")
  38. (declare-function org-mark-ring-goto "org.el")
  39. (declare-function org-mark-ring-push "org.el")
  40. (declare-function helm-interpret-value "helm.el")
  41. (declare-function helm-get-current-source "helm.el")
  42. (declare-function helm-source--cl--print-table "helm-source.el")
  43. (defvar helm-sources)
  44. (defvar helm-initial-frame)
  45. (defvar helm-current-position)
  46. (defvar wdired-old-marks)
  47. (defvar wdired-keep-marker-rename)
  48. (defvar wdired-allow-to-change-permissions)
  49. (defvar wdired-allow-to-redirect-links)
  50. (defvar helm-persistent-action-display-window)
  51. (defvar completion-flex-nospace)
  52. ;;; User vars.
  53. ;;
  54. (defcustom helm-file-globstar t
  55. "Same as globstar bash shopt option.
  56. When non--nil a pattern beginning with two stars will expand recursively.
  57. Directories expansion is not supported yet."
  58. :group 'helm
  59. :type 'boolean)
  60. (defcustom helm-yank-text-at-point-function nil
  61. "The function used to forward point with `helm-yank-text-at-point'.
  62. With a nil value, fallback to default `forward-word'.
  63. The function should take one arg, an integer like `forward-word'.
  64. NOTE: Using `forward-symbol' here is not very useful as it is already
  65. provided by \\<helm-map>\\[next-history-element]."
  66. :type 'function
  67. :group 'helm)
  68. (defcustom helm-scroll-amount nil
  69. "Scroll amount when scrolling other window in a helm session.
  70. It is used by `helm-scroll-other-window'
  71. and `helm-scroll-other-window-down'.
  72. If you prefer scrolling line by line, set this value to 1."
  73. :group 'helm
  74. :type 'integer)
  75. (defcustom helm-help-full-frame t
  76. "Display help window in full frame when non nil.
  77. Even when `nil' probably the same result (full frame)
  78. can be reach by tweaking `display-buffer-alist' but it is
  79. much more convenient to use a simple boolean value here."
  80. :type 'boolean
  81. :group 'helm-help)
  82. (defvar helm-ff--boring-regexp nil)
  83. (defun helm-ff--setup-boring-regex (var val)
  84. (set var val)
  85. (setq helm-ff--boring-regexp
  86. (cl-loop with last = (car (last val))
  87. for r in (butlast val)
  88. if (string-match "\\$\\'" r)
  89. concat (concat r "\\|") into result
  90. else concat (concat r "$\\|") into result
  91. finally return
  92. (concat result last
  93. (if (string-match "\\$\\'" last) "" "$")))))
  94. (defcustom helm-boring-file-regexp-list
  95. (mapcar (lambda (f)
  96. (let ((rgx (regexp-quote f)))
  97. (if (string-match-p "[^/]$" f)
  98. ;; files: e.g .o => \\.o$
  99. (concat rgx "$")
  100. ;; directories: e.g .git/ => \.git\\(/\\|$\\)
  101. (concat (substring rgx 0 -1) "\\(/\\|$\\)"))))
  102. completion-ignored-extensions)
  103. "A list of regexps matching boring files.
  104. This list is build by default on `completion-ignored-extensions'.
  105. The directory names should end with \"/?\" e.g. \"\\.git/?\" and the
  106. file names should end with \"$\" e.g. \"\\.o$\".
  107. These regexps may be used to match the entire path, not just the file
  108. name, so for example to ignore files with a prefix \".bak.\", use
  109. \"\\.bak\\..*$\" as the regexp.
  110. NOTE: When modifying this, be sure to use customize interface or the
  111. customize functions e.g. `customize-set-variable' and NOT `setq'."
  112. :group 'helm-files
  113. :type '(repeat (choice regexp))
  114. :set 'helm-ff--setup-boring-regex)
  115. (defcustom helm-describe-function-function 'describe-function
  116. "Function used to describe functions in Helm."
  117. :group 'helm-elisp
  118. :type 'function)
  119. (defcustom helm-describe-variable-function 'describe-variable
  120. "Function used to describe variables in Helm."
  121. :group 'helm-elisp
  122. :type 'function)
  123. ;;; Internal vars
  124. ;;
  125. (defvar helm-yank-point nil)
  126. (defvar helm-pattern ""
  127. "The input pattern used to update the helm buffer.")
  128. (defvar helm-buffer "*helm*"
  129. "Buffer showing completions.")
  130. (defvar helm-current-buffer nil
  131. "Current buffer when `helm' is invoked.")
  132. (defvar helm-suspend-update-flag nil)
  133. (defvar helm-action-buffer "*helm action*"
  134. "Buffer showing actions.")
  135. ;;; Compatibility
  136. ;;
  137. (defun helm-add-face-text-properties (beg end face &optional append object)
  138. "Add the face property to the text from START to END.
  139. It is a compatibility function which behave exactly like
  140. `add-face-text-property' if available otherwise like `add-text-properties'.
  141. When only `add-text-properties' is available APPEND is ignored."
  142. (if (fboundp 'add-face-text-property)
  143. (add-face-text-property beg end face append object)
  144. (add-text-properties beg end `(face ,face) object)))
  145. ;; Override `wdired-finish-edit'.
  146. ;; Fix emacs bug in `wdired-finish-edit' where
  147. ;; Wdired is not handling the case where `dired-directory' is a cons
  148. ;; cell instead of a string.
  149. (defun helm--advice-wdired-finish-edit ()
  150. (interactive)
  151. (wdired-change-to-dired-mode)
  152. (let ((changes nil)
  153. (errors 0)
  154. files-deleted
  155. files-renamed
  156. some-file-names-unchanged
  157. file-old file-new tmp-value)
  158. (save-excursion
  159. (when (and wdired-allow-to-redirect-links
  160. (fboundp 'make-symbolic-link))
  161. (setq tmp-value (wdired-do-symlink-changes))
  162. (setq errors (cdr tmp-value))
  163. (setq changes (car tmp-value)))
  164. (when (and wdired-allow-to-change-permissions
  165. (boundp 'wdired-col-perm)) ; could have been changed
  166. (setq tmp-value (wdired-do-perm-changes))
  167. (setq errors (+ errors (cdr tmp-value)))
  168. (setq changes (or changes (car tmp-value))))
  169. (goto-char (point-max))
  170. (while (not (bobp))
  171. (setq file-old (wdired-get-filename nil t))
  172. (when file-old
  173. (setq file-new (wdired-get-filename))
  174. (if (equal file-new file-old)
  175. (setq some-file-names-unchanged t)
  176. (setq changes t)
  177. (if (not file-new) ;empty filename!
  178. (push file-old files-deleted)
  179. (when wdired-keep-marker-rename
  180. (let ((mark (cond ((integerp wdired-keep-marker-rename)
  181. wdired-keep-marker-rename)
  182. (wdired-keep-marker-rename
  183. (cdr (assoc file-old wdired-old-marks)))
  184. (t nil))))
  185. (when mark
  186. (push (cons (substitute-in-file-name file-new) mark)
  187. wdired-old-marks))))
  188. (push (cons file-old (substitute-in-file-name file-new))
  189. files-renamed))))
  190. (forward-line -1)))
  191. (when files-renamed
  192. (setq errors (+ errors (wdired-do-renames files-renamed))))
  193. (if changes
  194. (progn
  195. ;; If we are displaying a single file (rather than the
  196. ;; contents of a directory), change dired-directory if that
  197. ;; file was renamed. (This ought to be generalized to
  198. ;; handle the multiple files case, but that's less trivial)
  199. ;; fixit [1].
  200. (cond ((and (stringp dired-directory)
  201. (not (file-directory-p dired-directory))
  202. (null some-file-names-unchanged)
  203. (= (length files-renamed) 1))
  204. (setq dired-directory (cdr (car files-renamed))))
  205. ;; Fix [1] i.e dired buffers created with
  206. ;; (dired '(foo f1 f2 f3)).
  207. ((and (consp dired-directory)
  208. (cdr dired-directory)
  209. files-renamed)
  210. (setcdr dired-directory
  211. ;; Replace in `dired-directory' files that have
  212. ;; been modified with their new name keeping
  213. ;; the ones that are unmodified at the same place.
  214. (cl-loop with old-to-rename = (mapcar 'car files-renamed)
  215. for f in (cdr dired-directory)
  216. if (member f old-to-rename)
  217. collect (assoc-default f files-renamed)
  218. else collect f))))
  219. ;; Re-sort the buffer if all went well.
  220. (unless (> errors 0) (revert-buffer))
  221. (let ((inhibit-read-only t))
  222. (dired-mark-remembered wdired-old-marks)))
  223. (let ((inhibit-read-only t))
  224. (remove-text-properties (point-min) (point-max)
  225. '(old-name nil end-name nil old-link nil
  226. end-link nil end-perm nil
  227. old-perm nil perm-changed nil))
  228. (message "(No changes to be performed)")))
  229. (when files-deleted
  230. (wdired-flag-for-deletion files-deleted))
  231. (when (> errors 0)
  232. (dired-log-summary (format "%d rename actions failed" errors) nil)))
  233. (set-buffer-modified-p nil)
  234. (setq buffer-undo-list nil))
  235. ;; Override `wdired-get-filename'.
  236. ;; Fix emacs bug in `wdired-get-filename' which returns the current
  237. ;; directory concatened with the filename i.e
  238. ;; "/home/you//home/you/foo" when filename is absolute in dired
  239. ;; buffer.
  240. ;; In consequence Wdired try to rename files even when buffer have
  241. ;; been modified and corrected, e.g delete one char and replace it so
  242. ;; that no change to file is done.
  243. ;; This also lead to ask confirmation for every files even when not
  244. ;; modified and when `wdired-use-interactive-rename' is nil.
  245. (defun helm--advice-wdired-get-filename (&optional no-dir old)
  246. ;; FIXME: Use dired-get-filename's new properties.
  247. (let (beg end file)
  248. (save-excursion
  249. (setq end (line-end-position))
  250. (beginning-of-line)
  251. (setq beg (next-single-property-change (point) 'old-name nil end))
  252. (unless (eq beg end)
  253. (if old
  254. (setq file (get-text-property beg 'old-name))
  255. ;; In the following form changed `(1+ beg)' to `beg' so that
  256. ;; the filename end is found even when the filename is empty.
  257. ;; Fixes error and spurious newlines when marking files for
  258. ;; deletion.
  259. (setq end (next-single-property-change beg 'end-name))
  260. (setq file (buffer-substring-no-properties (1+ beg) end)))
  261. ;; Don't unquote the old name, it wasn't quoted in the first place
  262. (and file (setq file (condition-case _err
  263. ;; emacs-25+
  264. (apply #'wdired-normalize-filename
  265. (list file (not old)))
  266. (wrong-number-of-arguments
  267. ;; emacs-24
  268. (wdired-normalize-filename file))))))
  269. (if (or no-dir old (and file (file-name-absolute-p file)))
  270. file
  271. (and file (> (length file) 0)
  272. (expand-file-name file (dired-current-directory)))))))
  273. ;;; Override `push-mark'
  274. ;;
  275. ;; Fix duplicates in `mark-ring' and `global-mark-ring' and update
  276. ;; buffers in `global-mark-ring' to recentest mark.
  277. (defun helm--advice-push-mark (&optional location nomsg activate)
  278. (unless (null (mark t))
  279. (let ((marker (copy-marker (mark-marker))))
  280. (setq mark-ring (cons marker (delete marker mark-ring))))
  281. (when (> (length mark-ring) mark-ring-max)
  282. ;; Move marker to nowhere.
  283. (set-marker (car (nthcdr mark-ring-max mark-ring)) nil)
  284. (setcdr (nthcdr (1- mark-ring-max) mark-ring) nil)))
  285. (set-marker (mark-marker) (or location (point)) (current-buffer))
  286. ;; Now push the mark on the global mark ring.
  287. (setq global-mark-ring (cons (copy-marker (mark-marker))
  288. ;; Avoid having multiple entries
  289. ;; for same buffer in `global-mark-ring'.
  290. (cl-loop with mb = (current-buffer)
  291. for m in global-mark-ring
  292. for nmb = (marker-buffer m)
  293. unless (eq mb nmb)
  294. collect m)))
  295. (when (> (length global-mark-ring) global-mark-ring-max)
  296. (set-marker (car (nthcdr global-mark-ring-max global-mark-ring)) nil)
  297. (setcdr (nthcdr (1- global-mark-ring-max) global-mark-ring) nil))
  298. (or nomsg executing-kbd-macro (> (minibuffer-depth) 0)
  299. (message "Mark set"))
  300. (when (or activate (not transient-mark-mode))
  301. (set-mark (mark t)))
  302. nil)
  303. (defcustom helm-advice-push-mark t
  304. "Override `push-mark' with a version avoiding duplicates when non nil."
  305. :group 'helm
  306. :type 'boolean
  307. :set (lambda (var val)
  308. (set var val)
  309. (if val
  310. (advice-add 'push-mark :override #'helm--advice-push-mark)
  311. (advice-remove 'push-mark #'helm--advice-push-mark))))
  312. ;; This the version of Emacs-27 written by Stefan
  313. (defun helm-advice--ffap-read-file-or-url (prompt guess)
  314. (or guess (setq guess default-directory))
  315. (if (ffap-url-p guess)
  316. (read-string prompt guess nil nil t)
  317. (unless (ffap-file-remote-p guess)
  318. (setq guess (abbreviate-file-name (expand-file-name guess))))
  319. (read-file-name prompt (file-name-directory guess) nil nil
  320. (file-name-nondirectory guess))))
  321. ;;; Macros helper.
  322. ;;
  323. (defmacro helm-with-gensyms (symbols &rest body)
  324. "Bind the SYMBOLS to fresh uninterned symbols and eval BODY."
  325. (declare (indent 1))
  326. `(let ,(mapcar (lambda (s)
  327. ;; Use cl-gensym here instead of make-symbol
  328. ;; to ensure a symbol that have a live that go
  329. ;; beyond the live of its macro have different name.
  330. ;; i.e symbols created with `with-helm-temp-hook'
  331. ;; should have random names.
  332. `(,s (cl-gensym (symbol-name ',s))))
  333. symbols)
  334. ,@body))
  335. ;;; Command loop helper
  336. ;;
  337. (defun helm-this-command ()
  338. "Returns the actual command in action.
  339. Like `this-command' but return the real command,
  340. and not `exit-minibuffer' or other unwanted functions."
  341. (cl-loop with bl = '(helm-maybe-exit-minibuffer
  342. helm-confirm-and-exit-minibuffer
  343. helm-exit-minibuffer
  344. exit-minibuffer)
  345. for count from 1 to 50
  346. for btf = (backtrace-frame count)
  347. for fn = (cl-second btf)
  348. if (and
  349. ;; In some case we may have in the way an
  350. ;; advice compiled resulting in byte-code,
  351. ;; ignore it (Issue #691).
  352. (symbolp fn)
  353. (commandp fn)
  354. (not (memq fn bl)))
  355. return fn
  356. else
  357. if (and (eq fn 'call-interactively)
  358. (> (length btf) 2))
  359. return (cadr (cdr btf))))
  360. ;;; Iterators
  361. ;;
  362. (cl-defmacro helm-position (item seq &key test all)
  363. "A simple and faster replacement of CL `position'.
  364. Returns ITEM first occurence position found in SEQ.
  365. When SEQ is a string, ITEM have to be specified as a char.
  366. Argument TEST when unspecified default to `eq'.
  367. When argument ALL is non--nil return a list of all ITEM positions
  368. found in SEQ."
  369. (let ((key (if (stringp seq) 'across 'in)))
  370. `(cl-loop with deftest = 'eq
  371. for c ,key ,seq
  372. for index from 0
  373. when (funcall (or ,test deftest) c ,item)
  374. if ,all collect index into ls
  375. else return index
  376. finally return ls)))
  377. (defun helm-iter-list (seq)
  378. "Return an iterator object from SEQ."
  379. (let ((lis seq))
  380. (lambda ()
  381. (let ((elm (car lis)))
  382. (setq lis (cdr lis))
  383. elm))))
  384. (defun helm-iter-circular (seq)
  385. "Infinite iteration on SEQ."
  386. (let ((lis seq))
  387. (lambda ()
  388. (let ((elm (car lis)))
  389. (setq lis (pcase lis (`(,_ . ,ll) (or ll seq))))
  390. elm))))
  391. (cl-defun helm-iter-sub-next-circular (seq elm &key (test 'eq))
  392. "Infinite iteration of SEQ starting at ELM."
  393. (let* ((pos (1+ (helm-position elm seq :test test)))
  394. (sub (append (nthcdr pos seq) (cl-subseq seq 0 pos)))
  395. (iterator (helm-iter-circular sub)))
  396. (lambda ()
  397. (helm-iter-next iterator))))
  398. (defun helm-iter-next (iterator)
  399. "Return next elm of ITERATOR."
  400. (and iterator (funcall iterator)))
  401. ;;; Anaphoric macros.
  402. ;;
  403. (defmacro helm-aif (test-form then-form &rest else-forms)
  404. "Anaphoric version of `if'.
  405. Like `if' but set the result of TEST-FORM in a temporary variable called `it'.
  406. THEN-FORM and ELSE-FORMS are then excuted just like in `if'."
  407. (declare (indent 2) (debug t))
  408. `(let ((it ,test-form))
  409. (if it ,then-form ,@else-forms)))
  410. (defmacro helm-awhile (sexp &rest body)
  411. "Anaphoric version of `while'.
  412. Same usage as `while' except that SEXP is bound to
  413. a temporary variable called `it' at each turn.
  414. An implicit nil block is bound to the loop so usage
  415. of `cl-return' is possible to exit the loop."
  416. (declare (indent 1) (debug t))
  417. (helm-with-gensyms (flag)
  418. `(let ((,flag t))
  419. (cl-block nil
  420. (while ,flag
  421. (helm-aif ,sexp
  422. (progn ,@body)
  423. (setq ,flag nil)))))))
  424. (defmacro helm-acond (&rest clauses)
  425. "Anaphoric version of `cond'.
  426. In each clause of CLAUSES, the result of the car of clause
  427. is stored in a temporary variable called `it' and usable in the cdr
  428. of this same clause. Each `it' variable is independent of its clause.
  429. The usage is the same as `cond'."
  430. (declare (debug cond))
  431. (unless (null clauses)
  432. (helm-with-gensyms (sym)
  433. (let ((clause1 (car clauses)))
  434. `(let ((,sym ,(car clause1)))
  435. (helm-aif ,sym
  436. (if (cdr ',clause1)
  437. (progn ,@(cdr clause1))
  438. it)
  439. (helm-acond ,@(cdr clauses))))))))
  440. (defmacro helm-aand (&rest conditions)
  441. "Anaphoric version of `and'.
  442. Each condition is bound to a temporary variable called `it' which is
  443. usable in next condition."
  444. (declare (debug (&rest form)))
  445. (cond ((null conditions) t)
  446. ((null (cdr conditions)) (car conditions))
  447. (t `(helm-aif ,(car conditions)
  448. (helm-aand ,@(cdr conditions))))))
  449. (defmacro helm-acase (expr &rest clauses)
  450. "A simple anaphoric `cl-case' implementation handling strings.
  451. EXPR is bound to a temporary variable called `it' which is usable in
  452. CLAUSES to refer to EXPR.
  453. NOTE: Duplicate keys in CLAUSES are deliberately not handled.
  454. \(fn EXPR (KEYLIST BODY...)...)"
  455. (declare (indent 1) (debug (form &rest (sexp body))))
  456. (unless (null clauses)
  457. (let ((clause1 (car clauses)))
  458. `(let ((key ',(car clause1))
  459. (it ,expr))
  460. (if (or (equal it key)
  461. (and (listp key) (member it key))
  462. (eq key t))
  463. (progn ,@(cdr clause1))
  464. (helm-acase it ,@(cdr clauses)))))))
  465. ;;; Fuzzy matching routines
  466. ;;
  467. (defsubst helm--mapconcat-pattern (pattern)
  468. "Transform string PATTERN in regexp for further fuzzy matching.
  469. e.g helm.el$
  470. => \"[^h]*h[^e]*e[^l]*l[^m]*m[^.]*[.][^e]*e[^l]*l$\"
  471. ^helm.el$
  472. => \"helm[.]el$\"."
  473. (let ((ls (split-string-and-unquote pattern "")))
  474. (if (string= "^" (car ls))
  475. ;; Exact match.
  476. (mapconcat (lambda (c)
  477. (if (and (string= c "$")
  478. (string-match "$\\'" pattern))
  479. c (regexp-quote c)))
  480. (cdr ls) "")
  481. ;; Fuzzy match.
  482. (mapconcat (lambda (c)
  483. (if (and (string= c "$")
  484. (string-match "$\\'" pattern))
  485. c (format "[^%s]*%s" c (regexp-quote c))))
  486. ls ""))))
  487. (defsubst helm--collect-pairs-in-string (string)
  488. (cl-loop for str on (split-string string "" t) by 'cdr
  489. when (cdr str)
  490. collect (list (car str) (cadr str))))
  491. ;;; Help routines.
  492. ;;
  493. (defun helm-help-internal (bufname insert-content-fn)
  494. "Show long message during `helm' session in BUFNAME.
  495. INSERT-CONTENT-FN is the function that insert
  496. text to be displayed in BUFNAME."
  497. (let ((winconf (current-frame-configuration))
  498. (hframe (selected-frame)))
  499. (with-selected-frame helm-initial-frame
  500. (select-frame-set-input-focus helm-initial-frame)
  501. (unwind-protect
  502. (progn
  503. (setq helm-suspend-update-flag t)
  504. (set-buffer (get-buffer-create bufname))
  505. (switch-to-buffer bufname)
  506. (when helm-help-full-frame (delete-other-windows))
  507. (delete-region (point-min) (point-max))
  508. (org-mode)
  509. (org-mark-ring-push) ; Put mark at bob
  510. (save-excursion
  511. (funcall insert-content-fn))
  512. (buffer-disable-undo)
  513. (helm-help-event-loop))
  514. (raise-frame hframe)
  515. (setq helm-suspend-update-flag nil)
  516. (set-frame-configuration winconf)))))
  517. (defun helm-help-scroll-up (amount)
  518. (condition-case _err
  519. (scroll-up-command amount)
  520. (beginning-of-buffer nil)
  521. (end-of-buffer nil)))
  522. (defun helm-help-scroll-down (amount)
  523. (condition-case _err
  524. (scroll-down-command amount)
  525. (beginning-of-buffer nil)
  526. (end-of-buffer nil)))
  527. (defun helm-help-next-line ()
  528. (condition-case _err
  529. (call-interactively #'next-line)
  530. (beginning-of-buffer nil)
  531. (end-of-buffer nil)))
  532. (defun helm-help-previous-line ()
  533. (condition-case _err
  534. (call-interactively #'previous-line)
  535. (beginning-of-buffer nil)
  536. (end-of-buffer nil)))
  537. (defun helm-help-toggle-mark ()
  538. (if (region-active-p)
  539. (deactivate-mark)
  540. (push-mark nil nil t)))
  541. ;; For movement of cursor in help buffer we need to call interactively
  542. ;; commands for impaired people using a synthetizer (#1347).
  543. (defun helm-help-event-loop ()
  544. (let ((prompt (propertize
  545. "[SPC,C-v,next:ScrollUp b,M-v,prior:ScrollDown TAB:Cycle M-TAB:All C-s/r:Isearch q:Quit]"
  546. 'face 'helm-helper))
  547. scroll-error-top-bottom
  548. (iter-org-state (helm-iter-circular '(1 (16) (64)))))
  549. (helm-awhile (read-key prompt)
  550. (cl-case it
  551. ((?\C-v ? next) (helm-help-scroll-up helm-scroll-amount))
  552. ((?\M-v ?b prior) (helm-help-scroll-down helm-scroll-amount))
  553. (?\C-s (isearch-forward))
  554. (?\C-r (isearch-backward))
  555. (?\C-a (call-interactively #'move-beginning-of-line))
  556. (?\C-e (call-interactively #'move-end-of-line))
  557. ((?\C-f right) (call-interactively #'forward-char))
  558. ((?\C-b left) (call-interactively #'backward-char))
  559. ((?\C-n down) (helm-help-next-line))
  560. ((?\C-p up) (helm-help-previous-line))
  561. (?\M-a (call-interactively #'backward-sentence))
  562. (?\M-e (call-interactively #'forward-sentence))
  563. (?\M-f (call-interactively #'forward-word))
  564. (?\M-b (call-interactively #'backward-word))
  565. (?\M-> (call-interactively #'end-of-buffer))
  566. (?\M-< (call-interactively #'beginning-of-buffer))
  567. (?\C- (helm-help-toggle-mark))
  568. (?\t (org-cycle))
  569. (?\C-m (ignore-errors (call-interactively #'org-open-at-point)))
  570. (?\C-& (ignore-errors (call-interactively #'org-mark-ring-goto)))
  571. (?\C-% (call-interactively #'org-mark-ring-push))
  572. (?\M-\t (pcase (helm-iter-next iter-org-state)
  573. ((pred numberp) (org-content))
  574. ((and state) (org-cycle state))))
  575. (?\M-w (copy-region-as-kill
  576. (region-beginning) (region-end))
  577. (deactivate-mark))
  578. (?q (cl-return))
  579. (t (ignore))))))
  580. ;;; Multiline transformer
  581. ;;
  582. (defun helm-multiline-transformer (candidates _source)
  583. (cl-loop with offset = (helm-interpret-value
  584. (assoc-default 'multiline (helm-get-current-source)))
  585. for i in candidates
  586. if (numberp offset)
  587. collect (cons (helm--multiline-get-truncated-candidate i offset) i)
  588. else collect i))
  589. (defun helm--multiline-get-truncated-candidate (candidate offset)
  590. "Truncate CANDIDATE when its length is > than OFFSET."
  591. (with-temp-buffer
  592. (insert candidate)
  593. (goto-char (point-min))
  594. (if (and offset
  595. (> (buffer-size) offset))
  596. (let ((end-str "[...]"))
  597. (concat
  598. (buffer-substring
  599. (point)
  600. (save-excursion
  601. (forward-char offset)
  602. (setq end-str (if (looking-at "\n")
  603. end-str (concat "\n" end-str)))
  604. (point)))
  605. end-str))
  606. (buffer-string))))
  607. ;;; List processing
  608. ;;
  609. (defun helm-flatten-list (seq &optional omit-nulls)
  610. "Return a list of all single elements of sublists in SEQ."
  611. (let (result)
  612. (cl-labels ((flatten (seq)
  613. (cl-loop
  614. for elm in seq
  615. if (and (or elm
  616. (null omit-nulls))
  617. (or (atom elm)
  618. (functionp elm)
  619. (and (consp elm)
  620. (cdr elm)
  621. (atom (cdr elm)))))
  622. do (push elm result)
  623. else do (flatten elm))))
  624. (flatten seq))
  625. (nreverse result)))
  626. (defun helm-mklist (obj)
  627. "If OBJ is a list \(but not lambda\), return itself.
  628. Otherwise make a list with one element."
  629. (if (and (listp obj) (not (functionp obj)))
  630. obj
  631. (list obj)))
  632. (cl-defun helm-fast-remove-dups (seq &key (test 'eq))
  633. "Remove duplicates elements in list SEQ.
  634. This is same as `remove-duplicates' but with memoisation.
  635. It is much faster, especially in large lists.
  636. A test function can be provided with TEST argument key.
  637. Default is `eq'.
  638. NOTE: Comparison of special elisp objects (e.g. markers etc...) fails
  639. because their printed representations which are stored in hash-table
  640. can't be compared with with the real object in SEQ.
  641. This is a bug in `puthash' which store the printable representation of
  642. object instead of storing the object itself, this to provide at the
  643. end a printable representation of hashtable itself."
  644. (cl-loop with cont = (make-hash-table :test test)
  645. for elm in seq
  646. unless (gethash elm cont)
  647. collect (puthash elm elm cont)))
  648. (defsubst helm--string-join (strings &optional separator)
  649. "Join all STRINGS using SEPARATOR."
  650. (mapconcat 'identity strings separator))
  651. (defun helm--concat-regexps (regexp-list)
  652. "Return a regexp which matches any of the regexps in REGEXP-LIST."
  653. (if regexp-list
  654. (concat "\\(?:" (helm--string-join regexp-list "\\)\\|\\(?:") "\\)")
  655. "\\<\\>")) ; Match nothing
  656. (defun helm-skip-entries (seq black-regexp-list &optional white-regexp-list)
  657. "Remove entries which matches one of REGEXP-LIST from SEQ."
  658. (let ((black-regexp (helm--concat-regexps black-regexp-list))
  659. (white-regexp (helm--concat-regexps white-regexp-list)))
  660. (cl-loop for i in seq
  661. unless (and (stringp i)
  662. (string-match-p black-regexp i)
  663. (null
  664. (string-match-p white-regexp i)))
  665. collect i)))
  666. (defun helm-boring-directory-p (directory black-list)
  667. "Check if one regexp in BLACK-LIST match DIRECTORY."
  668. (helm-awhile (helm-basedir (directory-file-name
  669. (expand-file-name directory)))
  670. (when (string= it "/") (cl-return nil))
  671. (when (cl-loop for r in black-list
  672. thereis (string-match-p
  673. r (directory-file-name directory)))
  674. (cl-return t))
  675. (setq directory it)))
  676. (defun helm-shadow-entries (seq regexp-list)
  677. "Put shadow property on entries in SEQ matching a regexp in REGEXP-LIST."
  678. (let ((face 'italic))
  679. (cl-loop for i in seq
  680. if (cl-loop for regexp in regexp-list
  681. thereis (and (stringp i)
  682. (string-match regexp i)))
  683. collect (propertize i 'face face)
  684. else collect i)))
  685. (defun helm-remove-if-not-match (regexp seq)
  686. "Remove all elements of SEQ that don't match REGEXP."
  687. (cl-loop for s in seq
  688. for str = (cond ((symbolp s)
  689. (symbol-name s))
  690. ((consp s)
  691. (car s))
  692. (t s))
  693. when (string-match-p regexp str)
  694. collect s))
  695. (defun helm-remove-if-match (regexp seq)
  696. "Remove all elements of SEQ that match REGEXP."
  697. (cl-loop for s in seq
  698. for str = (cond ((symbolp s)
  699. (symbol-name s))
  700. ((consp s)
  701. (car s))
  702. (t s))
  703. unless (string-match-p regexp str)
  704. collect s))
  705. (defun helm-transform-mapcar (function args)
  706. "`mapcar' for candidate-transformer.
  707. ARGS is (cand1 cand2 ...) or ((disp1 . real1) (disp2 . real2) ...)
  708. \(helm-transform-mapcar 'upcase '(\"foo\" \"bar\"))
  709. => (\"FOO\" \"BAR\")
  710. \(helm-transform-mapcar 'upcase '((\"1st\" . \"foo\") (\"2nd\" . \"bar\")))
  711. => ((\"1st\" . \"FOO\") (\"2nd\" . \"BAR\"))
  712. "
  713. (cl-loop for arg in args
  714. if (consp arg)
  715. collect (cons (car arg) (funcall function (cdr arg)))
  716. else
  717. collect (funcall function arg)))
  718. (defun helm-append-at-nth (seq elm index)
  719. "Append ELM at INDEX in SEQ."
  720. (let ((len (length seq)))
  721. (cond ((> index len) (setq index len))
  722. ((< index 0) (setq index 0)))
  723. (if (zerop index)
  724. (append elm seq)
  725. (cl-loop for i in seq
  726. for count from 1 collect i
  727. when (= count index)
  728. if (listp elm) append elm
  729. else collect elm))))
  730. (defun helm-source-by-name (name &optional sources)
  731. "Get a Helm source in SOURCES by NAME.
  732. Optional argument SOURCES is a list of Helm sources which default to
  733. `helm-sources'."
  734. (cl-loop with src-list = (if sources
  735. (cl-loop for src in sources
  736. collect (if (listp src)
  737. src
  738. (symbol-value src)))
  739. helm-sources)
  740. for source in src-list
  741. thereis (and (string= name (assoc-default 'name source)) source)))
  742. (defun helm-make-actions (&rest args)
  743. "Build an alist with (NAME . ACTION) elements with each pairs in ARGS.
  744. Where NAME is a string or a function returning a string or nil and ACTION
  745. a function.
  746. If NAME returns nil the pair is skipped.
  747. \(fn NAME ACTION ...)"
  748. (cl-loop for (name fn) on args by #'cddr
  749. when (functionp name)
  750. do (setq name (funcall name))
  751. when name
  752. collect (cons name fn)))
  753. ;;; Strings processing.
  754. ;;
  755. (defun helm-stringify (elm)
  756. "Return the representation of ELM as a string.
  757. ELM can be a string, a number or a symbol."
  758. (cl-typecase elm
  759. (string elm)
  760. (number (number-to-string elm))
  761. (symbol (symbol-name elm))))
  762. (defun helm-substring (str width)
  763. "Return the substring of string STR from 0 to WIDTH.
  764. Handle multibyte characters by moving by columns."
  765. (with-temp-buffer
  766. (save-excursion
  767. (insert str))
  768. (move-to-column width)
  769. (buffer-substring (point-at-bol) (point))))
  770. (defun helm-substring-by-width (str width &optional endstr)
  771. "Truncate string STR to end at column WIDTH.
  772. Similar to `truncate-string-to-width'.
  773. Add ENDSTR at end of truncated STR.
  774. Add spaces at end if needed to reach WIDTH when STR is shorter than WIDTH."
  775. (cl-loop for ini-str = str
  776. then (substring ini-str 0 (1- (length ini-str)))
  777. for sw = (string-width ini-str)
  778. when (<= sw width) return
  779. (concat ini-str endstr (make-string (- width sw) ? ))))
  780. (defun helm-string-multibyte-p (str)
  781. "Check if string STR contains multibyte characters."
  782. (cl-loop for c across str
  783. thereis (> (char-width c) 1)))
  784. (defun helm-get-pid-from-process-name (process-name)
  785. "Get pid from running process PROCESS-NAME."
  786. (cl-loop with process-list = (list-system-processes)
  787. for pid in process-list
  788. for process = (assoc-default 'comm (process-attributes pid))
  789. when (and process (string-match process-name process))
  790. return pid))
  791. (defun helm-ff-find-printers ()
  792. "Return a list of available printers on Unix systems."
  793. (when (executable-find "lpstat")
  794. (let ((printer-list (with-temp-buffer
  795. (call-process "lpstat" nil t nil "-a")
  796. (split-string (buffer-string) "\n"))))
  797. (cl-loop for p in printer-list
  798. for printer = (car (split-string p))
  799. when printer
  800. collect printer))))
  801. (defun helm-region-active-p ()
  802. (and transient-mark-mode mark-active (/= (mark) (point))))
  803. (defun helm-quote-whitespace (candidate)
  804. "Quote whitespace, if some, in string CANDIDATE."
  805. (replace-regexp-in-string " " "\\\\ " candidate))
  806. (defun helm-current-line-contents ()
  807. "Current line string without properties."
  808. (buffer-substring-no-properties (point-at-bol) (point-at-eol)))
  809. (defun helm--replace-regexp-in-buffer-string (regexp rep str &optional fixedcase literal subexp start)
  810. "Replace REGEXP by REP in string STR.
  811. Same as `replace-regexp-in-string' but handle properly REP as
  812. function with SUBEXP specified.
  813. e.g
  814. (helm--replace-regexp-in-buffer-string \"e\\\\(m\\\\)acs\" 'upcase \"emacs\" t nil 1)
  815. => \"eMacs\"
  816. (replace-regexp-in-string \"e\\\\(m\\\\)acs\" 'upcase \"emacs\" t nil 1)
  817. => \"eEMACSacs\"
  818. Also START argument behave as expected unlike
  819. `replace-regexp-in-string'.
  820. e.g
  821. (helm--replace-regexp-in-buffer-string \"f\" \"r\" \"foofoo\" t nil nil 3)
  822. => \"fooroo\"
  823. (replace-regexp-in-string \"f\" \"r\" \"foofoo\" t nil nil 3)
  824. => \"roo\"
  825. Unlike `replace-regexp-in-string' this function is buffer-based
  826. implemented i.e replacement is computed inside a temp buffer, so
  827. REGEXP should be used differently than with
  828. `replace-regexp-in-string'.
  829. NOTE: This function is used internally for
  830. `helm-ff-query-replace-on-filenames' and builded for this.
  831. You should use `replace-regexp-in-string' instead unless the behavior
  832. of this function is really needed."
  833. (with-temp-buffer
  834. (insert str)
  835. (goto-char (or start (point-min)))
  836. (while (re-search-forward regexp nil t)
  837. (replace-match (cond ((and (functionp rep) subexp)
  838. (funcall rep (match-string subexp)))
  839. ((functionp rep)
  840. (funcall rep str))
  841. (t rep))
  842. fixedcase literal nil subexp))
  843. (buffer-string)))
  844. (defun helm-url-unhex-string (str)
  845. "Same as `url-unhex-string' but ensure STR is completely decoded."
  846. (setq str (or str ""))
  847. (with-temp-buffer
  848. (save-excursion (insert str))
  849. (while (re-search-forward "%[A-Za-z0-9]\\{2\\}" nil t)
  850. (replace-match (byte-to-string (string-to-number
  851. (substring (match-string 0) 1)
  852. 16))
  853. t t)
  854. ;; Restart from beginning until string is completely decoded.
  855. (goto-char (point-min)))
  856. (decode-coding-string (buffer-string) 'utf-8)))
  857. (defun helm-read-answer (prompt answer-list)
  858. "Prompt user for an answer.
  859. Arg PROMPT is the prompt to present user the different possible
  860. answers, ANSWER-LIST is a list of strings.
  861. If user enter an answer which is one of ANSWER-LIST return this
  862. answer, otherwise keep prompting for a valid answer.
  863. Note that answer should be a single char, only short answer are
  864. accepted.
  865. Example:
  866. (let ((answer (helm-read-answer
  867. \"answer [y,n,!,q]: \"
  868. '(\"y\" \"n\" \"!\" \"q\"))))
  869. (pcase answer
  870. (\"y\" \"yes\")
  871. (\"n\" \"no\")
  872. (\"!\" \"all\")
  873. (\"q\" \"quit\")))
  874. "
  875. (helm-awhile (string
  876. (read-key (propertize prompt 'face 'minibuffer-prompt)))
  877. (if (member it answer-list)
  878. (cl-return it)
  879. (message "Please answer by %s" (mapconcat 'identity answer-list ", "))
  880. (sit-for 1))))
  881. ;;; Symbols routines
  882. ;;
  883. (defun helm-symbolify (str-or-sym)
  884. "Get symbol of STR-OR-SYM."
  885. (if (symbolp str-or-sym)
  886. str-or-sym
  887. (intern str-or-sym)))
  888. (defun helm-symbol-name (obj)
  889. (if (or (and (consp obj) (functionp obj))
  890. (byte-code-function-p obj))
  891. "Anonymous"
  892. (symbol-name obj)))
  893. (defun helm-describe-class (class)
  894. "Display documentation of Eieio CLASS, a symbol or a string."
  895. (advice-add 'cl--print-table :override #'helm-source--cl--print-table)
  896. (unwind-protect
  897. (let ((helm-describe-function-function 'describe-function))
  898. (helm-describe-function class))
  899. (advice-remove 'cl--print-table #'helm-source--cl--print-table)))
  900. (defun helm-describe-function (func)
  901. "Display documentation of FUNC, a symbol or string."
  902. (cl-letf (((symbol-function 'message) #'ignore))
  903. (funcall helm-describe-function-function (helm-symbolify func))))
  904. (defun helm-describe-variable (var)
  905. "Display documentation of VAR, a symbol or a string."
  906. (cl-letf (((symbol-function 'message) #'ignore))
  907. (funcall helm-describe-variable-function (helm-symbolify var))))
  908. (defun helm-describe-face (face)
  909. "Display documentation of FACE, a symbol or a string."
  910. (let ((faces (helm-marked-candidates)))
  911. (cl-letf (((symbol-function 'message) #'ignore))
  912. (describe-face (if (cdr faces)
  913. (mapcar 'helm-symbolify faces)
  914. (helm-symbolify face))))))
  915. (defun helm-elisp--persistent-help (candidate fun &optional name)
  916. "Used to build persistent actions describing CANDIDATE with FUN.
  917. Argument NAME is used internally to know which command to use when
  918. symbol CANDIDATE refers at the same time to variable and a function.
  919. See `helm-elisp-show-help'."
  920. (let ((hbuf (get-buffer (help-buffer))))
  921. (cond ((helm-follow-mode-p)
  922. (if name
  923. (funcall fun candidate name)
  924. (funcall fun candidate)))
  925. ((or (and (helm-attr 'help-running-p)
  926. (string= candidate (helm-attr 'help-current-symbol))))
  927. (progn
  928. ;; When started from a help buffer,
  929. ;; Don't kill this buffer as it is helm-current-buffer.
  930. (unless (equal hbuf helm-current-buffer)
  931. (kill-buffer hbuf)
  932. (set-window-buffer (get-buffer-window hbuf)
  933. ;; It is generally
  934. ;; helm-current-buffer but it may
  935. ;; be another buffer when helm have
  936. ;; been started from a dedicated window.
  937. (if helm--buffer-in-new-frame-p
  938. helm-current-buffer
  939. helm-persistent-action-window-buffer)))
  940. (helm-attrset 'help-running-p nil))
  941. ;; Force running update hook to may be delete
  942. ;; helm-persistent-action-display-window, this is done in
  943. ;; helm-persistent-action-display-window (the function).
  944. (unless helm--buffer-in-new-frame-p
  945. (helm-update (regexp-quote (helm-get-selection)))))
  946. (t
  947. (if name
  948. (funcall fun candidate name)
  949. (funcall fun candidate))
  950. (helm-attrset 'help-running-p t)))
  951. (helm-attrset 'help-current-symbol candidate)))
  952. (defun helm-find-function (func)
  953. "FUNC is symbol or string."
  954. (find-function (helm-symbolify func)))
  955. (defun helm-find-variable (var)
  956. "VAR is symbol or string."
  957. (find-variable (helm-symbolify var)))
  958. (defun helm-find-face-definition (face)
  959. "FACE is symbol or string."
  960. (find-face-definition (helm-symbolify face)))
  961. (defun helm-kill-new (candidate &optional replace)
  962. "CANDIDATE is symbol or string.
  963. See `kill-new' for argument REPLACE."
  964. (kill-new (helm-stringify candidate) replace))
  965. ;;; Modes
  966. ;;
  967. (defun helm-same-major-mode-p (start-buffer alist)
  968. "Decide if current-buffer is related to START-BUFFER.
  969. Argument ALIST is an alist of associated major modes."
  970. ;; START-BUFFER is the current-buffer where we start searching.
  971. ;; Determine the major-mode of START-BUFFER as `cur-maj-mode'.
  972. ;; Each time the loop go in another buffer we try from this buffer
  973. ;; to determine if its `major-mode' is:
  974. ;; - same as the `cur-maj-mode'
  975. ;; - derived from `cur-maj-mode' and from
  976. ;; START-BUFFER if its mode is derived from the one in START-BUFFER.
  977. ;; - have an assoc entry (major-mode . cur-maj-mode)
  978. ;; - have an rassoc entry (cur-maj-mode . major-mode)
  979. ;; - check if one of these entries inherit from another one in
  980. ;; `alist'.
  981. (let* ((cur-maj-mode (with-current-buffer start-buffer major-mode))
  982. (maj-mode major-mode)
  983. (c-assoc-mode (assq cur-maj-mode alist))
  984. (c-rassoc-mode (rassq cur-maj-mode alist))
  985. (o-assoc-mode (assq major-mode alist))
  986. (o-rassoc-mode (rassq major-mode alist))
  987. (cdr-c-assoc-mode (cdr c-assoc-mode))
  988. (cdr-o-assoc-mode (cdr o-assoc-mode)))
  989. (or (eq major-mode cur-maj-mode)
  990. (derived-mode-p cur-maj-mode)
  991. (with-current-buffer start-buffer
  992. (derived-mode-p maj-mode))
  993. (or (eq cdr-c-assoc-mode major-mode)
  994. (eq (car c-rassoc-mode) major-mode)
  995. (eq (cdr (assq cdr-c-assoc-mode alist))
  996. major-mode)
  997. (eq (car (rassq cdr-c-assoc-mode alist))
  998. major-mode))
  999. (or (eq cdr-o-assoc-mode cur-maj-mode)
  1000. (eq (car o-rassoc-mode) cur-maj-mode)
  1001. (eq (cdr (assq cdr-o-assoc-mode alist))
  1002. cur-maj-mode)
  1003. (eq (car (rassq cdr-o-assoc-mode alist))
  1004. cur-maj-mode)))))
  1005. ;;; Files routines
  1006. ;;
  1007. (defun helm-file-name-sans-extension (filename)
  1008. "Same as `file-name-sans-extension' but remove all extensions."
  1009. (helm-aif (file-name-sans-extension filename)
  1010. ;; Start searching at index 1 for files beginning with a dot (#1335).
  1011. (if (string-match "\\." (helm-basename it) 1)
  1012. (helm-file-name-sans-extension it)
  1013. it)))
  1014. (defun helm-basename (fname &optional ext)
  1015. "Print FNAME with any leading directory components removed.
  1016. If specified, also remove filename extension EXT.
  1017. Arg EXT can be specified as a string with or without dot,
  1018. in this case it should match file-name-extension.
  1019. It can also be non-nil (`t') in this case no checking
  1020. of file-name-extension is done and the extension is removed
  1021. unconditionally."
  1022. (let ((non-essential t))
  1023. (if (and ext (or (string= (file-name-extension fname) ext)
  1024. (string= (file-name-extension fname t) ext)
  1025. (eq ext t))
  1026. (not (file-directory-p fname)))
  1027. (file-name-sans-extension (file-name-nondirectory fname))
  1028. (file-name-nondirectory (directory-file-name fname)))))
  1029. (defun helm-basedir (fname)
  1030. "Return the base directory of filename ending by a slash."
  1031. (helm-aif (and fname
  1032. (or (and (string= fname "~") "~")
  1033. (file-name-directory fname)))
  1034. (file-name-as-directory it)))
  1035. (defun helm-current-directory ()
  1036. "Return current-directory name at point.
  1037. Useful in dired buffers when there is inserted subdirs."
  1038. (expand-file-name
  1039. (if (eq major-mode 'dired-mode)
  1040. (dired-current-directory)
  1041. default-directory)))
  1042. (defun helm-shadow-boring-files (files)
  1043. "Files matching `helm-boring-file-regexp' will be
  1044. displayed with the `file-name-shadow' face if available."
  1045. (helm-shadow-entries files helm-boring-file-regexp-list))
  1046. (defun helm-skip-boring-files (files)
  1047. "Files matching `helm-boring-file-regexp' will be skipped."
  1048. (helm-skip-entries files helm-boring-file-regexp-list))
  1049. (defun helm-skip-current-file (files)
  1050. "Current file will be skipped."
  1051. (remove (buffer-file-name helm-current-buffer) files))
  1052. (defun helm-w32-pathname-transformer (args)
  1053. "Change undesirable features of windows pathnames to ones more acceptable to
  1054. other candidate transformers."
  1055. (if (eq system-type 'windows-nt)
  1056. (helm-transform-mapcar
  1057. (lambda (x)
  1058. (replace-regexp-in-string
  1059. "/cygdrive/\\(.\\)" "\\1:"
  1060. (replace-regexp-in-string "\\\\" "/" x)))
  1061. args)
  1062. args))
  1063. (defun helm-w32-prepare-filename (file)
  1064. "Convert filename FILE to something usable by external w32 executables."
  1065. (replace-regexp-in-string ; For UNC paths
  1066. "/" "\\"
  1067. (replace-regexp-in-string ; Strip cygdrive paths
  1068. "/cygdrive/\\(.\\)" "\\1:"
  1069. file nil nil) nil t))
  1070. (defun helm-w32-shell-execute-open-file (file)
  1071. (with-no-warnings
  1072. (w32-shell-execute "open" (helm-w32-prepare-filename file))))
  1073. ;; Same as `vc-directory-exclusion-list'.
  1074. (defvar helm-walk-ignore-directories
  1075. '("SCCS/" "RCS/" "CVS/" "MCVS/" ".svn/" ".git/" ".hg/" ".bzr/"
  1076. "_MTN/" "_darcs/" "{arch}/" ".gvfs/"))
  1077. (defsubst helm--dir-file-name (file dir)
  1078. (expand-file-name
  1079. (substring file 0 (1- (length file))) dir))
  1080. (defsubst helm--dir-name-p (str)
  1081. (char-equal (aref str (1- (length str))) ?/))
  1082. (cl-defun helm-walk-directory (directory &key (path 'basename)
  1083. directories
  1084. match skip-subdirs
  1085. noerror)
  1086. "Walk through DIRECTORY tree.
  1087. Argument PATH can be one of basename, relative, full, or a function
  1088. called on file name, default to basename.
  1089. Argument DIRECTORIES when `t' return also directories names,
  1090. otherwise skip directories names, with a value of `only' returns
  1091. only subdirectories, i.e files are skipped.
  1092. Argument MATCH is a regexp matching files or directories.
  1093. Argument SKIP-SUBDIRS when `t' will skip `helm-walk-ignore-directories'
  1094. otherwise if it is given as a list of directories, this list will be used
  1095. instead of `helm-walk-ignore-directories'.
  1096. Argument NOERROR when `t' will skip directories which are not accessible."
  1097. (let ((fn (cl-case path
  1098. (basename 'file-name-nondirectory)
  1099. (relative 'file-relative-name)
  1100. (full 'identity)
  1101. (t path)))) ; A function.
  1102. (setq skip-subdirs (if (listp skip-subdirs)
  1103. skip-subdirs
  1104. helm-walk-ignore-directories))
  1105. (cl-labels ((ls-rec (dir)
  1106. (unless (file-symlink-p dir)
  1107. (cl-loop for f in (sort (file-name-all-completions "" dir)
  1108. 'string-lessp)
  1109. unless (member f '("./" "../"))
  1110. ;; A directory.
  1111. ;; Use `helm--dir-file-name' to remove the final slash.
  1112. ;; Needed to avoid infloop on directory symlinks.
  1113. if (and (helm--dir-name-p f)
  1114. (helm--dir-file-name f dir))
  1115. nconc
  1116. (unless (or (member f skip-subdirs)
  1117. (and noerror
  1118. (not (file-accessible-directory-p it))))
  1119. (if (and directories
  1120. (or (null match)
  1121. (string-match match f)))
  1122. (nconc (list (concat (funcall fn it) "/"))
  1123. (ls-rec it))
  1124. (ls-rec it)))
  1125. ;; A regular file.
  1126. else nconc
  1127. (when (and (null (eq directories 'only))
  1128. (or (null match) (string-match match f)))
  1129. (list (funcall fn (expand-file-name f dir))))))))
  1130. (ls-rec directory))))
  1131. (defun helm-file-expand-wildcards (pattern &optional full)
  1132. "Same as `file-expand-wildcards' but allow recursion.
  1133. Recursion happen when PATTERN starts with two stars.
  1134. Directories expansion is not supported."
  1135. (let ((bn (helm-basename pattern))
  1136. (case-fold-search nil))
  1137. (if (and helm-file-globstar
  1138. (string-match "\\`\\*\\{2\\}\\(.*\\)" bn))
  1139. (helm-walk-directory (helm-basedir pattern)
  1140. :path (cl-case full
  1141. (full 'full)
  1142. (relative 'relative)
  1143. ((basename nil) 'basename)
  1144. (t 'full))
  1145. :directories nil
  1146. :match (wildcard-to-regexp bn)
  1147. :skip-subdirs t)
  1148. (file-expand-wildcards pattern full))))
  1149. ;;; helm internals
  1150. ;;
  1151. (defun helm-set-pattern (pattern &optional noupdate)
  1152. "Set minibuffer contents to PATTERN.
  1153. if optional NOUPDATE is non-nil, helm buffer is not changed."
  1154. (with-selected-window (or (active-minibuffer-window) (minibuffer-window))
  1155. (delete-minibuffer-contents)
  1156. (insert pattern))
  1157. (when noupdate
  1158. (setq helm-pattern pattern)))
  1159. (defun helm-minibuffer-completion-contents ()
  1160. "Return the user input in a minibuffer before point as a string.
  1161. That is what completion commands operate on."
  1162. (buffer-substring (field-beginning) (point)))
  1163. (defmacro with-helm-buffer (&rest body)
  1164. "Eval BODY inside `helm-buffer'."
  1165. (declare (indent 0) (debug t))
  1166. `(with-current-buffer (helm-buffer-get)
  1167. ,@body))
  1168. (defmacro with-helm-current-buffer (&rest body)
  1169. "Eval BODY inside `helm-current-buffer'."
  1170. (declare (indent 0) (debug t))
  1171. `(with-current-buffer (or (and (buffer-live-p helm-current-buffer)
  1172. helm-current-buffer)
  1173. (setq helm-current-buffer
  1174. (current-buffer)))
  1175. ,@body))
  1176. (defun helm-buffer-get ()
  1177. "Return `helm-action-buffer' if shown otherwise `helm-buffer'."
  1178. (if (helm-action-window)
  1179. helm-action-buffer
  1180. helm-buffer))
  1181. (defun helm-window ()
  1182. "Window of `helm-buffer'."
  1183. (get-buffer-window (helm-buffer-get) 0))
  1184. (defun helm-action-window ()
  1185. "Window of `helm-action-buffer'."
  1186. (get-buffer-window helm-action-buffer 'visible))
  1187. (defmacro with-helm-window (&rest body)
  1188. "Be sure BODY is excuted in the helm window."
  1189. (declare (indent 0) (debug t))
  1190. `(with-selected-window (helm-window)
  1191. ,@body))
  1192. (defmacro helm-without-follow (&rest body)
  1193. "Ensure BODY runs without following.
  1194. I.e. when using `helm-next-line' and friends in BODY."
  1195. (declare (indent 0) (debug t))
  1196. `(cl-letf (((symbol-function 'helm-follow-mode-p)
  1197. (lambda (&optional _) nil)))
  1198. (let (helm-follow-mode-persistent)
  1199. (progn ,@body))))
  1200. ;; Completion styles related functions
  1201. ;;
  1202. (defun helm--setup-completion-styles-alist ()
  1203. (cl-pushnew '(helm helm-completion-try-completion
  1204. helm-completion-all-completions
  1205. "helm multi completion style.")
  1206. completion-styles-alist
  1207. :test 'equal)
  1208. (unless (assq 'flex completion-styles-alist)
  1209. ;; Add helm-fuzzy style only if flex is not available.
  1210. (cl-pushnew '(helm-flex helm-flex-completion-try-completion
  1211. helm-flex-completion-all-completions
  1212. "helm flex completion style.\nProvide flex matching for emacs-26.")
  1213. completion-styles-alist
  1214. :test 'equal)))
  1215. (defvar helm-blacklist-completion-styles '(emacs21 emacs22))
  1216. (defun helm--prepare-completion-styles (&optional nomode)
  1217. "Return a suitable list of styles for `completion-styles'."
  1218. ;; For `helm-completion-style' and `helm-completion-styles-alist'.
  1219. (require 'helm-mode)
  1220. (if (memq helm-completion-style '(helm helm-fuzzy))
  1221. ;; Keep default settings, but probably nil is fine as well.
  1222. '(basic partial-completion emacs22)
  1223. (or
  1224. (pcase (and (null nomode)
  1225. (with-helm-current-buffer
  1226. (cdr (assq major-mode helm-completion-styles-alist))))
  1227. (`(,_l . ,ll) ll))
  1228. ;; We need to have flex always behind helm, otherwise
  1229. ;; when matching against e.g. '(foo foobar foao frogo bar
  1230. ;; baz) with pattern "foo" helm style if before flex will
  1231. ;; return foo and foobar only defeating flex that would
  1232. ;; return foo foobar foao and frogo.
  1233. (let* ((wflex (car (or (assq 'flex completion-styles-alist)
  1234. (assq 'helm-flex completion-styles-alist))))
  1235. (styles (append (and (memq wflex completion-styles)
  1236. (list wflex))
  1237. (cl-loop for s in completion-styles
  1238. unless (or (memq s helm-blacklist-completion-styles)
  1239. (memq wflex completion-styles))
  1240. collect s))))
  1241. (helm-append-at-nth
  1242. styles '(helm)
  1243. (if (memq wflex completion-styles)
  1244. 1 0))))))
  1245. (defun helm-dynamic-completion (collection predicate &optional point metadata nomode)
  1246. "Build a function listing the possible completions of `helm-pattern' in COLLECTION.
  1247. Only the elements of COLLECTION that satisfy PREDICATE are considered.
  1248. Argument POINT is same as in `completion-all-completions' and is
  1249. meaningful only when using some kind of `completion-at-point'.
  1250. The return value is a list of completions that may be sorted by the
  1251. sort function provided by the completion-style in use (emacs-27 only),
  1252. otherwise (emacs-26) the sort function have to be provided if needed
  1253. either with a FCT function in source or by passing the sort function
  1254. with METADATA e.g. (metadata (display-sort-function . foo)).
  1255. Example:
  1256. (helm :sources (helm-build-sync-source \"test\"
  1257. :candidates (helm-dynamic-completion
  1258. '(foo bar baz foab)
  1259. 'symbolp)
  1260. :match-dynamic t)
  1261. :buffer \"*helm test*\")
  1262. When argument NOMODE is non nil don't use `completion-styles' as
  1263. specified in `helm-completion-styles-alist'."
  1264. (lambda ()
  1265. (let* ((completion-styles
  1266. (helm--prepare-completion-styles nomode))
  1267. (completion-flex-nospace t)
  1268. (compsfn (lambda (str pred _action)
  1269. (let* ((comps (completion-all-completions
  1270. str
  1271. (if (functionp collection)
  1272. (funcall collection str predicate t)
  1273. collection)
  1274. pred
  1275. (or point 0)
  1276. (or metadata '(metadata))))
  1277. (last-data (last comps))
  1278. (sort-fn (completion-metadata-get
  1279. metadata 'display-sort-function))
  1280. all)
  1281. (when (cdr last-data)
  1282. (setcdr last-data nil))
  1283. (setq all (copy-sequence comps))
  1284. (if sort-fn (funcall sort-fn all) all)))))
  1285. ;; Ensure circular objects are removed.
  1286. (complete-with-action t compsfn helm-pattern predicate))))
  1287. ;; Yank text at point.
  1288. ;;
  1289. ;;
  1290. (defun helm-yank-text-at-point (arg)
  1291. "Yank text at point in `helm-current-buffer' into minibuffer."
  1292. (interactive "p")
  1293. (with-helm-current-buffer
  1294. (let ((fwd-fn (or helm-yank-text-at-point-function #'forward-word))
  1295. diff)
  1296. ;; Start to initial point if C-w have never been hit.
  1297. (unless helm-yank-point
  1298. (setq helm-yank-point (car helm-current-position)))
  1299. (save-excursion
  1300. (goto-char helm-yank-point)
  1301. (helm-set-pattern
  1302. (if (< arg 0)
  1303. (with-temp-buffer
  1304. (insert helm-pattern)
  1305. (let ((end (point-max)))
  1306. (goto-char end)
  1307. (funcall fwd-fn -1)
  1308. (setq diff (- end (point)))
  1309. (delete-region (point) end)
  1310. (buffer-string)))
  1311. (funcall fwd-fn arg)
  1312. (concat
  1313. ;; Allow yankink beyond eol allow inserting e.g long
  1314. ;; urls in mail buffers.
  1315. helm-pattern (replace-regexp-in-string
  1316. "\\`\n" ""
  1317. (buffer-substring-no-properties
  1318. helm-yank-point (point))))))
  1319. (setq helm-yank-point (if diff (- (point) diff) (point)))))))
  1320. (put 'helm-yank-text-at-point 'helm-only t)
  1321. (defun helm-undo-yank-text-at-point ()
  1322. "Undo last entry added by `helm-yank-text-at-point'."
  1323. (interactive)
  1324. (helm-yank-text-at-point -1))
  1325. (put 'helm-undo-yank-text-at-point 'helm-only t)
  1326. (defun helm-reset-yank-point ()
  1327. (setq helm-yank-point nil))
  1328. (add-hook 'helm-cleanup-hook 'helm-reset-yank-point)
  1329. (add-hook 'helm-after-initialize-hook 'helm-reset-yank-point)
  1330. ;;; Ansi
  1331. ;;
  1332. ;;
  1333. (defvar helm--ansi-color-regexp
  1334. "\033\\[\\(K\\|[0-9;]*m\\)")
  1335. (defvar helm--ansi-color-drop-regexp
  1336. "\033\\[\\([ABCDsuK]\\|[12][JK]\\|=[0-9]+[hI]\\|[0-9;]*[Hf]\\)")
  1337. (defun helm--ansi-color-apply (string)
  1338. "A version of `ansi-color-apply' immune to upstream changes.
  1339. Similar to the emacs-24.5 version without support to `ansi-color-context'
  1340. which is buggy in emacs.
  1341. Modify also `ansi-color-regexp' by using own variable `helm--ansi-color-regexp'
  1342. that match whole STRING.
  1343. This is needed to provide compatibility for both emacs-25 and emacs-24.5
  1344. as emacs-25 version of `ansi-color-apply' is partially broken."
  1345. (let ((start 0)
  1346. codes end escape-sequence
  1347. result colorized-substring)
  1348. ;; Find the next escape sequence.
  1349. (while (setq end (string-match helm--ansi-color-regexp string start))
  1350. (setq escape-sequence (match-string 1 string))
  1351. ;; Colorize the old block from start to end using old face.
  1352. (when codes
  1353. (put-text-property
  1354. start end 'font-lock-face (ansi-color--find-face codes) string))
  1355. (setq colorized-substring (substring string start end)
  1356. start (match-end 0))
  1357. ;; Eliminate unrecognized ANSI sequences.
  1358. (while (string-match helm--ansi-color-drop-regexp colorized-substring)
  1359. (setq colorized-substring
  1360. (replace-match "" nil nil colorized-substring)))
  1361. (push colorized-substring result)
  1362. ;; Create new face, by applying escape sequence parameters.
  1363. (setq codes (ansi-color-apply-sequence escape-sequence codes)))
  1364. ;; If the rest of the string should have a face, put it there.
  1365. (when codes
  1366. (put-text-property
  1367. start (length string)
  1368. 'font-lock-face (ansi-color--find-face codes) string))
  1369. ;; Save the remainder of the string to the result.
  1370. (if (string-match "\033" string start)
  1371. (push (substring string start (match-beginning 0)) result)
  1372. (push (substring string start) result))
  1373. (apply 'concat (nreverse result))))
  1374. (provide 'helm-lib)
  1375. ;; Local Variables:
  1376. ;; byte-compile-warnings: (not obsolete)
  1377. ;; coding: utf-8
  1378. ;; indent-tabs-mode: nil
  1379. ;; End:
  1380. ;;; helm-lib ends here