mirror of https://github.com/Chizi123/.emacs.d.git

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