summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debian/changelog6
-rw-r--r--helm-buffers.el3
-rw-r--r--helm-core-pkg.el2
-rw-r--r--helm-eshell.el110
-rw-r--r--helm-files.el186
-rw-r--r--helm-grep.el10
-rw-r--r--helm-help.el125
-rw-r--r--helm-id-utils.el1
-rw-r--r--helm-lib.el49
-rw-r--r--helm-mode.el32
-rw-r--r--helm-org.el89
-rw-r--r--helm-pkg.el4
-rw-r--r--helm-regexp.el8
-rw-r--r--helm-source.el20
-rw-r--r--helm-utils.el11
-rw-r--r--helm.el60
16 files changed, 530 insertions, 186 deletions
diff --git a/debian/changelog b/debian/changelog
index 4c50bd84..8742eff9 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+helm (2.8.2-1) unstable; urgency=medium
+
+ * New upstream release.
+
+ -- Sean Whitton <spwhitton@spwhitton.name> Tue, 22 Aug 2017 18:45:18 -0700
+
helm (2.8.1-1) unstable; urgency=medium
* New upstream release.
diff --git a/helm-buffers.el b/helm-buffers.el
index 78fd1028..b467f63b 100644
--- a/helm-buffers.el
+++ b/helm-buffers.el
@@ -569,7 +569,8 @@ i.e same color."
(defun helm-buffers--match-from-directory (candidate)
(let* ((cand (replace-regexp-in-string "^\\s-\\{1\\}" "" candidate))
(buf (get-buffer cand))
- (buf-fname (buffer-file-name buf))
+ (buf-fname (or (buffer-file-name buf)
+ (car-safe (rassoc buf dired-buffers))))
(regexps (cl-loop with pattern = helm-pattern
for p in (helm-mm-split-pattern pattern)
when (string-match "\\`/" p)
diff --git a/helm-core-pkg.el b/helm-core-pkg.el
index 964bdf60..fa75b04b 100644
--- a/helm-core-pkg.el
+++ b/helm-core-pkg.el
@@ -1,6 +1,6 @@
;;; helm-core-pkg.el --- define helm-core for package.el
-(define-package "helm-core" "2.8.1"
+(define-package "helm-core" "2.8.2"
"Development files for Helm"
'((emacs "24.4")
(async "1.9.2"))
diff --git a/helm-eshell.el b/helm-eshell.el
index 77de8c36..c01e3c04 100644
--- a/helm-eshell.el
+++ b/helm-eshell.el
@@ -22,7 +22,7 @@
;; (lambda ()
;; (eshell-cmpl-initialize)
;; (define-key eshell-mode-map [remap eshell-pcomplete] 'helm-esh-pcomplete)
-;; (define-key eshell-mode-map (kbd "M-p") 'helm-eshell-history)))
+;; (define-key eshell-mode-map (kbd "M-r") 'helm-eshell-history)))
;;; Code:
@@ -108,20 +108,25 @@ The function that call this should set `helm-ec-target' to thing at point."
(delete-region (point) pt)))
(when (string-match "\\`\\*" helm-ec-target) (insert "*"))
(let ((marked (helm-marked-candidates)))
- (insert
- (mapconcat
- (lambda (x)
- (cond ((string-match "\\`~/?" helm-ec-target)
- ;; Strip out the first escape char added by
- ;; `comint-quote-filename' before "~" (Issue #1803).
- (substring (comint-quote-filename (abbreviate-file-name x)) 1))
- ((string-match "\\`/" helm-ec-target)
- (comint-quote-filename x))
- (t
- (concat (and (string-match "\\`[.]/" helm-ec-target) "./")
- (comint-quote-filename
- (file-relative-name x))))))
- marked " "))))
+ (prog1 t ;; Makes helm returns t on action.
+ (insert
+ (mapconcat
+ (lambda (x)
+ (cond ((string-match "\\`~/" helm-ec-target)
+ ;; Strip out the first escape char added by
+ ;; `comint-quote-filename' before "~" (Issue #1803).
+ (substring (comint-quote-filename (abbreviate-file-name x)) 1))
+ ((string-match "\\`/" helm-ec-target)
+ (comint-quote-filename x))
+ (t
+ (concat (and (string-match "\\`[.]/" helm-ec-target) "./")
+ (comint-quote-filename
+ (file-relative-name x))))))
+ marked " ")
+ (or (helm-aand (car (last marked))
+ (string-match-p "/\\'" it)
+ "")
+ " ")))))
(defun helm-esh-get-candidates ()
"Get candidates for eshell completion using `pcomplete'."
@@ -202,6 +207,9 @@ The function that call this should set `helm-ec-target' to thing at point."
"Helm class to define source for Eshell history.")
+
+(defvar helm-eshell--quit-flag nil)
+
;;;###autoload
(defun helm-esh-pcomplete ()
"Preconfigured helm to provide helm completion in eshell."
@@ -218,9 +226,11 @@ The function that call this should set `helm-ec-target' to thing at point."
(save-excursion
(eshell-backward-argument 1) (point))
end)))
+ (users-comp (string= target "~"))
(first (car args)) ; Maybe lisp delimiter "(".
last ; Will be the last but parsed by pcomplete.
- del-space)
+ del-space
+ del-dot)
(setq helm-ec-target (or target " ")
end (point)
;; Reset beg for `with-helm-show-completion'.
@@ -228,6 +238,11 @@ The function that call this should set `helm-ec-target' to thing at point."
(- end (length target)))
;; Nothing at point.
(progn (insert " ") (setq del-space t) (point))))
+ (when (string-match "\\`[~.]*.*/[.]\\'" target)
+ ;; Fix completion on
+ ;; "~/.", "~/[...]/.", and "../."
+ (delete-char -1) (setq del-dot t)
+ (setq helm-ec-target (substring helm-ec-target 0 (1- (length helm-ec-target)))))
(cond ((eq first ?\()
(helm-lisp-completion-or-file-name-at-point))
;; In eshell `pcomplete-parse-arguments' is called
@@ -240,16 +255,61 @@ The function that call this should set `helm-ec-target' to thing at point."
"\\`\\*" ""
(car (last (ignore-errors
(pcomplete-parse-arguments))))))
+ ;; Set helm-eshell--quit-flag to non-nil only on
+ ;; quit, this tells to not add final suffix when quitting
+ ;; helm.
+ (add-hook 'helm-quit-hook 'helm-eshell--quit-hook-fn)
(with-helm-show-completion beg end
- (or (helm :sources (helm-make-source "Eshell completions" 'helm-esh-source
- :fuzzy-match helm-eshell-fuzzy-match)
- :buffer "*helm pcomplete*"
- :keymap helm-esh-completion-map
- :resume 'noresume
- :input (and (stringp last)
- (helm-ff-set-pattern last)))
- (and del-space (looking-back "\\s-" (1- (point)))
- (delete-char -1))))))))
+ (unwind-protect
+ (or (helm :sources (helm-make-source "Eshell completions" 'helm-esh-source
+ :fuzzy-match helm-eshell-fuzzy-match)
+ :buffer "*helm pcomplete*"
+ :keymap helm-esh-completion-map
+ :resume 'noresume
+ :input (if (and (stringp last)
+ (not (string= last ""))
+ (not users-comp)
+ ;; Fix completion on
+ ;; "../" see #1832.
+ (or (file-exists-p last)
+ (helm-aand
+ (file-name-directory last)
+ (file-directory-p it))))
+ (if (and (file-directory-p last)
+ (string-match "\\`[~.]*.*/[.]\\'" target))
+ ;; Fix completion on
+ ;; "~/.", "~/[...]/.", and "../."
+ (expand-file-name
+ (concat (helm-basedir (file-name-as-directory last))
+ (regexp-quote (helm-basename target))))
+ (expand-file-name last))
+ ;; Don't add "~" to input to
+ ;; provide completion on all
+ ;; users instead of only on
+ ;; current $HOME (#1832).
+ (unless users-comp last)))
+ ;; Delete removed dot on quit
+ (and del-dot (prog1 t (insert ".")))
+ ;; A space is needed to have completion, remove
+ ;; it when nothing found.
+ (and del-space (looking-back "\\s-" (1- (point)))
+ (delete-char -1))
+ (if (and (null helm-eshell--quit-flag)
+ (looking-back "[.]\\{1,2\\}\\'" (1- (point))))
+ (prog1 t (insert "/"))
+ ;; We need another flag for space here, but
+ ;; global to pass it to `helm-quit-hook', this
+ ;; space is added when point is just after
+ ;; previous completion and there is no
+ ;; more completion, see issue #1832.
+ (unless (or helm-eshell--quit-flag
+ (looking-back "/\\'" (1- (point))))
+ (prog1 t (insert " ")))))
+ (remove-hook 'helm-quit-hook 'helm-eshell--quit-hook-fn)
+ (setq helm-eshell--quit-flag nil)))))))
+
+(defun helm-eshell--quit-hook-fn ()
+ (setq helm-eshell--quit-flag t))
;;;###autoload
(defun helm-eshell-history ()
diff --git a/helm-files.el b/helm-files.el
index 21522629..553d7886 100644
--- a/helm-files.el
+++ b/helm-files.el
@@ -323,6 +323,11 @@ Of course you can also write your own function to do something else."
:group 'helm-files
:type 'function)
+(defcustom helm-modes-using-escaped-strings
+ '(eshell-mode shell-mode term-mode)
+ "Modes that requires string's insertion to be escaped."
+ :group 'helm-files
+ :type '(repeat symbol))
;;; Faces
;;
@@ -627,22 +632,46 @@ Should not be used among other sources.")
(message "Helm find files session bookmarked! ")))
(put 'helm-ff-bookmark-set 'helm-only t)
+(defcustom helm-dwim-target nil
+ "Default target directory for file actions.
+
+Define the directory where you want to start navigating for the target
+directory when copying, renaming etc... You can use the
+`default-directory' of `next-window', the current
+`default-directory' or have completion on all the directories
+belonging to each window."
+ :group 'helm-files
+ :type '(radio :tag "Define default target directory for file actions."
+ (const :tag "Directory belonging to next window" next-window)
+ (const :tag "Completion on directories belonging to each window" completion)
+ (const :tag "Use initial directory or `default-directory'" nil)))
+
(defun helm-dwim-target-directory ()
- "Return value of `default-directory' of buffer in other window.
-If there is only one window return the value of currently visited directory
-if found in `helm-ff-history' or fallback to `default-directory'
-of current buffer."
+ "Try to return a suitable directory according to `helm-dwim-target'."
(with-helm-current-buffer
- (let ((num-windows (length (remove (get-buffer-window helm-marked-buffer-name)
- (window-list)))))
+ (let* ((wins (remove (get-buffer-window helm-marked-buffer-name)
+ (window-list)))
+ (num-windows (length wins)))
(expand-file-name
- (if (> num-windows 1)
- (save-selected-window
- (other-window 1)
- default-directory)
- ;; Using the car of *ff-history allow
- ;; staying in the directory visited instead of current.
- (or (car-safe helm-ff-history) default-directory))))))
+ (cond ((and (> num-windows 1)
+ (eq helm-dwim-target 'completion))
+ (helm-comp-read "Browse target starting from: "
+ (append (list (or (car-safe helm-ff-history)
+ default-directory)
+ default-directory)
+ (cl-loop for w in wins collect
+ (with-selected-window w
+ default-directory)))))
+ ((and (> num-windows 1)
+ (eq helm-dwim-target 'next-window))
+ (with-selected-window (next-window)
+ default-directory))
+ ((or (= num-windows 1)
+ (null helm-dwim-target))
+ ;; Using the car of *ff-history allow
+ ;; staying in the directory visited instead of
+ ;; current.
+ (or (car-safe helm-ff-history) default-directory)))))))
(defun helm-ff--count-and-collect-dups (files)
(cl-loop with dups = (make-hash-table :test 'equal)
@@ -746,7 +775,8 @@ This reproduce the behavior of \"cp --backup=numbered from to\"."
(defun helm-find-files-ediff-files-1 (candidate &optional merge)
"Generic function to ediff/merge files in `helm-find-files'."
- (let* ((bname (helm-basename candidate))
+ (let* ((helm-dwim-target 'next-window)
+ (bname (helm-basename candidate))
(marked (helm-marked-candidates :with-wildcard t))
(prompt (if merge "Ediff Merge `%s' With File: "
"Ediff `%s' With File: "))
@@ -1075,30 +1105,43 @@ This doesn't replace inside the files, only modify filenames."
nil 'helm-ff-query-replace-history-from
(helm-basename (car candidates))))
(rep (read-string (format "Replace regexp `%s' with: " regexp)
- nil 'helm-ff-query-replace-history-to)))
+ nil 'helm-ff-query-replace-history-to))
+ subexp)
(cl-loop with query = "y"
with count = 0
with target = nil
for old in candidates
for new = (concat (helm-basedir old)
- (replace-regexp-in-string
- (cond ((string= regexp "%.")
- (regexp-quote
- (setq target (helm-basename old t))))
- ((string= regexp ".%")
- (regexp-quote
- (setq target (file-name-extension old))))
- ((string= regexp "%")
- (regexp-quote
- (setq target (helm-basename old))))
- (t regexp))
+ (helm--replace-regexp-in-buffer-string
+ (save-match-data
+ (cond ((string= regexp "%.")
+ (setq subexp 1)
+ (helm-ff--prepare-str-with-regexp
+ (setq target (helm-basename old t))))
+ ((string= regexp ".%")
+ (setq subexp 1)
+ (helm-ff--prepare-str-with-regexp
+ (setq target (file-name-extension old))))
+ ((string= regexp "%")
+ (regexp-quote
+ (setq target (helm-basename old))))
+ ((string-match "%:\\([0-9]+\\):\\([0-9]+\\)" regexp)
+ (setq subexp 1)
+ (let ((beg (match-string 1 regexp))
+ (end (match-string 2 regexp))
+ (str (helm-basename old)))
+ (setq target (substring str
+ (string-to-number beg)
+ (string-to-number end)))
+ (helm-ff--prepare-str-with-regexp str beg end)))
+ (t regexp)))
(save-match-data
(cond (;; Handle incremental
;; replacement with \# in
;; search and replace
;; feature in placeholder \@.
(string-match
- "\\\\@/\\(.*\\)/\\(\\(?99:.*\\)\\\\#\\)"
+ "\\\\@/\\(.*\\)/\\(\\(?99:.*\\)\\\\#\\)/"
rep)
(replace-regexp-in-string
(match-string 1 rep)
@@ -1131,11 +1174,11 @@ This doesn't replace inside the files, only modify filenames."
;; Search and replace in
;; placeholder. Doesn't
;; handle incremental here.
- ((string-match "\\\\@/\\(.*\\)/\\(.*\\)" rep)
+ ((string-match "\\\\@/\\(.*\\)/\\(.*\\)/" rep)
(replace-match (replace-regexp-in-string
(match-string 1 rep)
(match-string 2 rep)
- target)
+ target t)
t t rep))
;; Simple replacement by placeholder.
((string-match "\\\\@" rep)
@@ -1149,7 +1192,7 @@ This doesn't replace inside the files, only modify filenames."
;; Simple replacement with
;; whole replacement regexp.
(t rep)))
- (helm-basename old) t))
+ (helm-basename old) t nil subexp))
;; If `regexp' is not matched in `old'
;; `replace-regexp-in-string' will
;; return `old' unmodified.
@@ -1183,6 +1226,27 @@ This doesn't replace inside the files, only modify filenames."
(with-current-buffer (window-buffer (minibuffer-window))
(delete-minibuffer-contents)))
+(defun helm-ff--prepare-str-with-regexp (str &optional rep1 rep2)
+ ;; This is used in `helm-ff-query-replace-on-filenames' to prepare
+ ;; STR when REGEXP is specified as substring e.g %:1:3 in this case
+ ;; substring from 1 to 3 in STR will be enclosed with parenthesis to
+ ;; match this substring as a subexp e.g %:1:3 on string "emacs" will
+ ;; be replaced by "e\\(ma\\)cs" using subexp 1 like this:
+ ;; (helm--replace-regexp-in-buffer-string "e\\(ma\\)cs" "fo" "emacs" nil t 1)
+ ;; => "efocs"
+ ;; ^^
+ ;; Where "1" and "3" will be strings extracted with match-string
+ ;; from regexp and refered respectively in this function as REP1 and
+ ;; REP2.
+ (let* ((from (or (and rep1 (string-to-number rep1)) 0))
+ (to (or (and rep2 (string-to-number rep2)) (length str)))
+ (subexp (concat "\\(" (regexp-quote (substring str from to)) "\\)"))
+ (before-str (unless (zerop from)
+ (regexp-quote (substring str 0 from))))
+ (after-str (unless (= to (length str))
+ (regexp-quote (substring str to (length str))))))
+ (concat before-str subexp after-str)))
+
;; The action.
(defun helm-ff-query-replace-on-marked (_candidate)
(let ((marked (helm-marked-candidates :with-wildcard t)))
@@ -1886,11 +1950,16 @@ With a prefix arg toggle dired buffer to wdired mode."
;; symbol (`tramp-file-name') which is not needed as argument
;; for `tramp-make-tramp-file-name' so transform the cdr in
;; vector, and for 24.5 use directly the returned value.
- (cl-loop with v = (pcase (tramp-dissect-file-name fname)
- (`(,_l . ,ll) (vconcat ll))
- ((and vec (pred vectorp)) vec))
+ (cl-loop with v = (helm--tramp-cons-or-vector
+ (tramp-dissect-file-name fname))
for i across v collect i)))
+(defun helm--tramp-cons-or-vector (vector-or-cons)
+ "Return VECTOR-OR-CONS as a vector."
+ (pcase vector-or-cons
+ (`(,_l . ,ll) (vconcat ll))
+ ((and vec (pred vectorp)) vec)))
+
(defun helm-ff-get-tramp-methods ()
"Returns a list of the car of `tramp-methods'."
(or helm-ff--tramp-methods
@@ -2325,9 +2394,10 @@ Note that only existing directories are saved here."
(save-selected-window
(cl-loop for c in marked do
(progn (helm-preselect
- (if (and helm-ff-transformer-show-only-basename
- (not (helm-ff-dot-file-p c)))
- (helm-basename c) c))
+ (concat "^" (regexp-quote
+ (if (and helm-ff-transformer-show-only-basename
+ (not (helm-ff-dot-file-p c)))
+ (helm-basename c) c))))
(when (y-or-n-p
(format "Really Delete file `%s'? "
(abbreviate-file-name c)))
@@ -2341,9 +2411,9 @@ Note that only existing directories are saved here."
helm-visible-mark-overlays nil))
(helm-force-update
(let ((presel (helm-get-selection)))
- (regexp-quote (if (and helm-ff-transformer-show-only-basename
- (not (helm-ff-dot-file-p presel)))
- (helm-basename presel) presel)))))))
+ (concat "^" (regexp-quote (if (and helm-ff-transformer-show-only-basename
+ (not (helm-ff-dot-file-p presel)))
+ (helm-basename presel) presel))))))))
(defun helm-ff-kill-buffer-fname (candidate)
(let* ((buf (get-file-buffer candidate))
@@ -2823,24 +2893,31 @@ If a prefix arg is given or `helm-follow-mode' is on open file."
guess)
(string-match-p
"\\`\\(/\\|[[:lower:][:upper:]]:/\\)"
- guess)))))
+ guess))))
+ (escape-fn (with-helm-current-buffer
+ (if (memq major-mode
+ helm-modes-using-escaped-strings)
+ #'shell-quote-argument #'identity))))
(set-text-properties 0 (length candidate) nil candidate)
- (if (and guess (not (string= guess ""))
- (or (string-match "^\\(~/\\|/\\|[[:lower:][:upper:]]:/\\)"
- guess)
- (file-exists-p candidate)))
- (progn
- (delete-region beg end)
- (insert (cond (full-path-p
- (expand-file-name candidate))
- ((string= (match-string 1 guess) "~/")
- (abbreviate-file-name candidate))
- (t (file-relative-name candidate)))))
- (insert (cond ((equal helm-current-prefix-arg '(4))
+ (insert
+ (funcall escape-fn
+ (if (and guess (not (string= guess ""))
+ (or (string-match
+ "^\\(~/\\|/\\|[[:lower:][:upper:]]:/\\)"
+ guess)
+ (file-exists-p candidate)))
+ (prog1
+ (cond (full-path-p
+ (expand-file-name candidate))
+ ((string= (match-string 1 guess) "~/")
+ (abbreviate-file-name candidate))
+ (t (file-relative-name candidate)))
+ (delete-region beg end))
+ (cond ((equal helm-current-prefix-arg '(4))
(abbreviate-file-name candidate))
((equal helm-current-prefix-arg '(16))
(file-relative-name candidate))
- (t candidate))))))))
+ (t candidate)))))))))
(cl-defun helm-find-files-history (&key (comp-read t))
"The `helm-find-files' history.
@@ -4118,7 +4195,8 @@ It allows additionally to delete more than one connection at once."
:candidate-transformer (lambda (candidates)
(cl-loop for v in candidates
for name = (apply #'tramp-make-tramp-file-name
- (cl-loop for i across v collect i))
+ (cl-loop with v = (helm--tramp-cons-or-vector v)
+ for i across v collect i))
when (or (processp (tramp-get-connection-process v))
(buffer-live-p (get-buffer (tramp-buffer-name v))))
collect (cons name v)))
diff --git a/helm-grep.el b/helm-grep.el
index b96170cf..4092debc 100644
--- a/helm-grep.el
+++ b/helm-grep.el
@@ -549,6 +549,7 @@ It is intended to use as a let-bound variable, DON'T set this globaly.")
proc-name
(helm-get-candidate-number)
(- (float-time) start-time))
+ (helm-maybe-show-help-echo)
(with-helm-window
(setq mode-line-format
`(" " mode-line-buffer-identification " "
@@ -968,6 +969,12 @@ These extensions will be added to command line with --include arg of grep."
;;; Set up source
;;
;;
+(defvar helm-grep-before-init-hook nil
+ "Hook that runs before initialization of the helm buffer.")
+
+(defvar helm-grep-after-init-hook nil
+ "Hook that runs after initialization of the helm buffer.")
+
(defclass helm-grep-class (helm-source-async)
((candidates-process :initform 'helm-grep-collect-candidates)
(filter-one-by-one :initform 'helm-grep-filter-one-by-one)
@@ -991,6 +998,8 @@ These extensions will be added to command line with --include arg of grep."
(persistent-action :initform 'helm-grep-persistent-action)
(persistent-help :initform "Jump to line (`C-u' Record in mark ring)")
(requires-pattern :initform 2)
+ (before-init-hook :initform 'helm-grep-before-init-hook)
+ (after-init-hook :initform 'helm-grep-after-init-hook)
(group :initform 'helm-grep)))
(defvar helm-source-grep nil)
@@ -1461,6 +1470,7 @@ if available with current AG version."
proc-name
(helm-get-candidate-number)
(- (float-time) start-time))
+ (helm-maybe-show-help-echo)
(with-helm-window
(setq mode-line-format
`(" " mode-line-buffer-identification " "
diff --git a/helm-help.el b/helm-help.el
index 9ec7267f..7a444059 100644
--- a/helm-help.el
+++ b/helm-help.el
@@ -426,66 +426,111 @@ The directory selection with \"**/\" like bash shopt globstar option is not supp
*** Query replace regexp on filenames
-WARNING: This is designed to work ONLY in current directory, i.e
- your marked files have to be from the same directory.
- So do not mark files in different directories, [[Using wildcard to select multiple files][recursive globbing]] e.g \"**.txt\"
- is not supported as well for same reasons.
+Allow replacing different parts of file's basename with something else.
-You can rename your marked files by replacing only part of filenames matching
-a regexp.
+When calling this action you will be prompted like in `query-replace' with two prompts,
+one for the matching part of text to replace and another one for the replacement text,
+however several facilities are provided to make the two prompts more powerfull.
-e.g Rename recursively all files with \".JPG\" extension to \".jpg\":
-Use the helm-file-globstar feature described in previous section by
-entering at end of helm-find-files pattern \"**.JPG\", then hit `M-%`,
-at first prompt enter \"JPG\", at second \"jpg\" and hit `RET`.
+**** Syntax available in first prompt:
-Shortcut for basename without extension, only extension or all are available:
+In addition to a simple regexp, these shortcuts are availables:
- Basename without extension => \"%.\"
- Only extension => \".%\"
-- All => \"%\"
+- Substring => \"%:<from>:<to>\"
+- Whole basename => \"%\"
+
+**** Syntax available in second prompt
+
+In addition to a simple string to use as replacement, here what you can use:
+
+- A placeholder refering to what you have selected in first prompt: \"\\@\"
+After this placeholder you can use a search and replace syntax ala sed:
+
+ \"\\@/<regexp>/<replacement>/
+
+You can remove substring part of string represented by placeholder:
+
+ \"\\@:<from>:<to>\"
+
+- A special character representing a number which is incremented: \"\\#\"
+
+- shortcut for `upcase', `downcase' and `capitalize'
+are available, respectively `%u', `%d' and `%c'.
+
+**** Usage with examples
+
+***** Rename recursively all files with \".JPG\" extension to \".jpg\":
+
+Use the `helm-file-globstar' feature described in [[Using wildcard to select multiple files][recursive globbing]] by
+entering at end of helm-find-files pattern \"**.JPG\", then hit \\<helm-map>\\[helm-ff-query-replace-on-filenames],
+at first prompt enter \"JPG\", at second \"jpg\" and hit `RET`.
So in the example above you could do instead:
At first prompt enter \".%\", at second \"jpg\" and hit `RET`.
+
Note that when using this instead of using \"JPG\" at first prompt, all extensions
will be renamed to \"jpg\" even if the extension of one of the files is e.g \"png\".
+If you want to keep the original extension you can use at first prompt \".%\",
+and at second \"%d\" (downcase).
-If you want to rename a serie of files from number 001 to 00x use \\# inside the replacement
-string when you will be prompted for it.
+***** Rename a serie of files from number 001 to 00x
+
+Use \\# inside the second prompt
e.g To rename the files \"foo.jpg\" \"bar.jpg\" and \"baz.jpg\"
to \"foo-001.jpg\" \"foo-002.jpg\" \"foo-003.jpg\"
Use as replace regexp \"%.\" and as replacement string \"foo-\\#\".
-When \"%\", \".%\" or \"%\" are used, \"\\@\" can be used as a placeholder which
-remember those values.
-
e.g To rename the files \"foo.jpg\" \"bar.jpg\" and \"baz.jpg\"
to \"foo-001.jpg\" \"bar-002.jpg\" \"baz-003.jpg\"
Use as replace regexp \"%.\" and as replacement string \"\\@-\\#\".
-Modifying the placeholder (\\@) is possible
-\(in contrast of renaming the whole placeholder with something else) with two methods:
+***** Replace a range of text
+
+ \"%:<from>:<to>\"
+
+e.g To rename files \"foo.jpg\" \"bar.jpg\" and \"baz.jpg\"
+ to \"fOo.jpg\" \"bAr.jpg\" and \"bAz.jpg\"
+
+Use as replace regexp \"%:1:2\" and as replacement string \"%u\" (aka upcase).
+
+NOTE that you CANNOT use \"%.\" and \".%\" along with substring replacement.
+
+***** Modifying string from the placeholder (\\@)
- By substring, i.e using only the substring of placeholder:
\\@:<from>:<to>
e.g \\@:0:2 replaces from beginning to second char of placeholder
Note that length of placeholder is used for <to> when <to> is not specified
e.g \\@:2: replaces from second char of placeholder to end
+ This is a quick way to strip out one part of string.
- By search and replace:
- \\@/<regexp>/<replacement>
- e.g \\@/foo/bar replaces \"foo\" in placeholder by \"bar\"
+ \\@/<regexp>/<replacement>/
+ e.g \\@/foo/bar/ replaces \"foo\" in placeholder by \"bar\"
Incremental replacement is also handled in <replacement>
- e.g \\@/foo/-\\# replaces \"foo\" in placeholder by 001, 002 etc...
+ e.g \\@/foo/-\\#/ replaces \"foo\" in placeholder by 001, 002 etc...
-In the second prompt (replace regexp with) shortcut for `upcase', `downcase' and `capitalize'
-are available, respectively `%u', `%d' and `%c'.
+***** Clash in replacements (avoid overwriting files)
+
+In all these replacements you may endup with same names as replacement, in such cases
+helm takes care of numbering the files that would overwrite precedent file, e.g:
+Say you remove in files \"emacs-m1.txt\" \"emacs-m2.txt\" and \"emacs-m3.txt\" the \"-m<n>\" part
+you would endup with three files named \"emacs.txt\", the second renaming overwriting first file,
+and the third renaming overwriting second file and so on, instead helm will rename like
+\"emacs(1).txt\" and \"emacs(2).txt\" second an third file.
-Note also that unlike the [[Serial renaming][serial rename]] actions the renamed files stay in their initial directory
-and are not renamed to current directory, IOW use this (\\#) to rename files inside the same directory.
+***** Query-replace on filenames vs serial rename actions
+
+Note also that unlike the [[Serial renaming][serial rename]] actions
+the renamed files stay in their initial directory and are not renamed
+to current directory, so usage of \\# to serial rename files make
+sense only for files inside the same directory even it continues renaming
+files with an incremental number in next directories.
*** Serial renaming
@@ -507,6 +552,12 @@ Note that wildcards are supported as well, so you can use e.g \"*.txt\" to selec
in current directory or \"**.txt\" to select all files recursively from current directory
\(See [[Using wildcard to select multiple files]] section above).
+*** Defining default target directory for copying/renaming etc.. according to `helm-dwim-target'
+
+You can customize `helm-dwim-target' to behave differently according to the windows open
+in your current frame.
+Default is to provide completion on the directories belonging to these windows if some.
+
*** Copying renaming asynchronously
If you use async library (if you have installed helm from MELPA you do) you can enable
@@ -516,7 +567,7 @@ Note that even when async is enabled, running a copy/rename action with a prefix
will execute action synchronously, it will follow also the first file of the marked files
in its destination directory.
-When `dired-async-mode' is enabled and additional action named \"Backup files\" will be
+When `dired-async-mode' is enabled, an additional action named \"Backup files\" will be
available (such command is not available in emacs natively).
See [[Using wildcard to select multiple files]] for details.
@@ -1330,18 +1381,22 @@ but this will not push a new entry with concatenated candidates in kill-ring.
*** Refiling
-The heading to refile will be the one you were at when starting helm
-session, and the place to refile this heading will be the selected
-candidate (i.e the candidate at point in helm buffer). If you want to
-refile another one, move to it in helm buffer, mark it, then move to
-the candidate of your choice to refile at this place.
-NOTE that of course if you have marked more than one candidate,
-all the subsequent candidates will be ignored.
+You can refile one heading or multiple headings at a time.
+
+To refile one heading, move the point to the entry you want to
+refile, and run \\[helm-org-in-buffer-headings]. Then select the
+heading you want to refile to, and press \\[C-c w], or select the
+refile action from the actions menu.
+
+To refile multiple headings, run \\[helm-org-in-buffer-headings]
+and mark the headings you want to refile. Then select the
+heading you want to refile to (without marking it), and press
+\\[C-c w], or select the refile action from the actions menu.
** Commands
\\<helm-org-headings-map>
\\[helm-org-run-open-heading-in-indirect-buffer]\t\tOpen heading in indirect buffer.
-\\[helm-org-run-heading-refile]\t\tRefile current heading to selection.
+\\[helm-org-run-refile-heading-to]\t\tRefile current heading to selection, or marked headings to selection.
\\[helm-org-run-insert-link-to-heading-at-marker]\t\tInsert link at point to selection."
)
diff --git a/helm-id-utils.el b/helm-id-utils.el
index 2f249180..670da281 100644
--- a/helm-id-utils.el
+++ b/helm-id-utils.el
@@ -55,6 +55,7 @@ MacPorts to install id-utils, it should be `gid32'."
(set-process-sentinel
proc (lambda (_process event)
(when (string= event "finished\n")
+ (helm-maybe-show-help-echo)
(with-helm-window
(setq mode-line-format
'(" " mode-line-buffer-identification " "
diff --git a/helm-lib.el b/helm-lib.el
index 6e9b5502..350d83c4 100644
--- a/helm-lib.el
+++ b/helm-lib.el
@@ -334,6 +334,7 @@ In each clause of CLAUSES, the result of the car of clause
is stored in a temporary variable called `it' and usable in the cdr
of this same clause. Each `it' variable is independent of its clause.
The usage is the same as `cond'."
+ (declare (debug cond))
(unless (null clauses)
(helm-with-gensyms (sym)
(let ((clause1 (car clauses)))
@@ -346,6 +347,7 @@ The usage is the same as `cond'."
(defmacro helm-aand (&rest conditions)
"Anaphoric version of `and'."
+ (declare (debug (&rest form)))
(cond ((null conditions) t)
((null (cdr conditions)) (car conditions))
(t `(helm-aif ,(car conditions)
@@ -736,6 +738,51 @@ Add spaces at end if needed to reach WIDTH when STR is shorter than WIDTH."
"Current line string without properties."
(buffer-substring-no-properties (point-at-bol) (point-at-eol)))
+(defun helm--replace-regexp-in-buffer-string (regexp rep str &optional fixedcase literal subexp start)
+ "Replace REGEXP by REP in string STR.
+
+Same as `replace-regexp-in-string' but handle properly REP as
+function with SUBEXP specified.
+
+e.g
+
+ (helm--replace-regexp-in-buffer-string \"e\\\\(m\\\\)acs\" 'upcase \"emacs\" t nil 1)
+ => \"eMacs\"
+
+ (replace-regexp-in-string \"e\\\\(m\\\\)acs\" 'upcase \"emacs\" t nil 1)
+ => \"eEMACSacs\"
+
+Also START argument behave as expected unlike
+`replace-regexp-in-string'.
+
+e.g
+
+ (helm--replace-regexp-in-buffer-string \"f\" \"r\" \"foofoo\" t nil nil 3)
+ => \"fooroo\"
+
+ (replace-regexp-in-string \"f\" \"r\" \"foofoo\" t nil nil 3)
+ => \"roo\"
+
+Unlike `replace-regexp-in-string' this function is buffer-based
+implemented i.e replacement is computed inside a temp buffer, so
+REGEXP should be used differently than with
+`replace-regexp-in-string'.
+
+NOTE: This function is used internally for
+`helm-ff-query-replace-on-filenames' and builded for this.
+You should use `replace-regexp-in-string' instead unless the behavior
+of this function is really needed."
+ (with-temp-buffer
+ (insert str)
+ (goto-char (or start (point-min)))
+ (while (re-search-forward regexp nil t)
+ (replace-match (cond ((and (functionp rep) subexp)
+ (funcall rep (match-string subexp)))
+ ((functionp rep)
+ (funcall rep str))
+ (t rep))
+ fixedcase literal nil subexp))
+ (buffer-string)))
;;; Symbols routines
;;
@@ -1067,6 +1114,8 @@ That is what completion commands operate on."
(buffer-string)))
(funcall fwd-fn arg)
(concat
+ ;; Allow yankink beyond eol allow inserting e.g long
+ ;; urls in mail buffers.
helm-pattern (replace-regexp-in-string
"\\`\n" ""
(buffer-substring-no-properties
diff --git a/helm-mode.el b/helm-mode.el
index bcf12959..491594c4 100644
--- a/helm-mode.el
+++ b/helm-mode.el
@@ -176,7 +176,9 @@ and all functions belonging in this list from `minibuffer-setup-hook'."
(let ((debug-on-quit nil))
(signal 'quit nil)))
-(cl-defun helm-comp-read-get-candidates (collection &optional test sort-fn alistp (input ""))
+(cl-defun helm-comp-read-get-candidates (collection &optional
+ test sort-fn alistp
+ (input helm-pattern))
"Convert COLLECTION to list removing elements that don't match TEST.
See `helm-comp-read' about supported COLLECTION arguments.
@@ -186,7 +188,8 @@ ALISTP when non--nil will not use `all-completions' to collect
candidates because it doesn't handle alists correctly for helm.
i.e In `all-completions' the car of each pair is used as value.
In helm we want to use the cdr instead like \(display . real\),
-so we return the alist as it is with no transformation by all-completions.
+so we return the alist as it is with no transformation by
+`all-completions'.
e.g
@@ -204,6 +207,16 @@ e.g
See docstring of `all-completions' for more info.
+INPUT is the string you want to complete against, defaulting to
+`helm-pattern' which is the value of what you enter in minibuffer.
+Note that when using a function as COLLECTION this value will be
+available with the input argument of the function only when using a
+sync source from `helm-comp-read', i.e not using
+`:candidates-in-buffer', otherwise the function is called only once
+with an empty string as value for `helm-pattern' because
+`helm-pattern' is not yet computed, which is what we want otherwise
+data would not be fully collected at init time.
+
If COLLECTION is an `obarray', a TEST should be needed. See `obarray'."
;; Ensure COLLECTION is computed from `helm-current-buffer'
;; because some functions used as COLLECTION work
@@ -242,13 +255,13 @@ If COLLECTION is an `obarray', a TEST should be needed. See `obarray'."
;; but special cases like `find-file-at-point' do it.
;; Handle here specially such cases.
((and (functionp collection) minibuffer-completing-file-name)
- (cl-loop for f in (funcall collection helm-pattern test t)
+ (cl-loop for f in (funcall collection input test t)
unless (member f '("./" "../"))
- if (string-match helm--url-regexp helm-pattern)
+ if (string-match helm--url-regexp input)
collect f
else
collect (concat (file-name-as-directory
- (helm-basedir helm-pattern)) f)))
+ (helm-basedir input)) f)))
((functionp collection)
(funcall collection input test t))
((and alistp (null test)) collection)
@@ -479,7 +492,12 @@ that use `helm-comp-read' See `helm-M-x' for example."
(get-candidates
(lambda ()
(let ((cands (helm-comp-read-get-candidates
- collection test sort alistp)))
+ collection test sort alistp
+ ;; This should not be needed as
+ ;; `helm-pattern' is not yet computed when
+ ;; calling this from :init when
+ ;; candidates-in-buffer is in use.
+ (if candidates-in-buffer "" helm-pattern))))
(helm-cr-default default cands))))
(history-get-candidates
(lambda ()
@@ -688,7 +706,7 @@ It should be used when candidate list don't need to rebuild dynamically."
;; candidates-in-buffer that reuse precedent data (files) which is wrong.
;; So (re)calculate collection outside of main helm-session.
(let* ((cands (helm-comp-read-get-candidates
- collection test nil nil "")))
+ collection test nil nil)))
(helm-completing-read-default-1 prompt cands test require-match
init hist default inherit-input-method
name buffer t)))
diff --git a/helm-org.el b/helm-org.el
index 56765aa7..b86e7bc6 100644
--- a/helm-org.el
+++ b/helm-org.el
@@ -20,6 +20,10 @@
(require 'helm)
(require 'helm-utils)
(require 'org)
+
+;; Load org-with-point-at macro when compiling
+(eval-when-compile
+ (require 'org-macs))
(declare-function org-agenda-switch-to "org-agenda.el")
@@ -58,7 +62,7 @@ Note this have no effect in `helm-org-in-buffer-headings'."
(defcustom helm-org-headings-actions
'(("Go to heading" . helm-org-goto-marker)
("Open in indirect buffer `C-c i'" . helm-org--open-heading-in-indirect-buffer)
- ("Refile current or marked heading to selection `C-c w`" . helm-org-heading-refile)
+ ("Refile heading(s) (multiple-marked-to-selected, or current-to-selected) `C-c w`" . helm-org--refile-heading-to)
("Insert link to this heading `C-c l`" . helm-org-insert-link-to-heading-at-marker))
"Default actions alist for
`helm-source-org-headings-for-files'."
@@ -70,6 +74,12 @@ Note this have no effect in `helm-org-in-buffer-headings'."
:type 'boolean
:group 'helm-org)
+(defcustom helm-org-ignore-autosaves nil
+ "Ignore autosave files when starting `helm-org-agenda-files-headings'."
+ :type 'boolean
+ :group 'helm-org)
+
+
;;; Org capture templates
;;
;;
@@ -80,7 +90,7 @@ Note this have no effect in `helm-org-in-buffer-headings'."
collect (cons (nth 1 template) (nth 0 template)))
:action '(("Do capture" . (lambda (template-shortcut)
(org-capture nil template-shortcut))))))
-
+
;;; Org headings
;;
;;
@@ -111,7 +121,7 @@ Note this have no effect in `helm-org-in-buffer-headings'."
(let ((map (make-sparse-keymap)))
(set-keymap-parent map helm-map)
(define-key map (kbd "C-c i") 'helm-org-run-open-heading-in-indirect-buffer)
- (define-key map (kbd "C-c w") 'helm-org-run-heading-refile)
+ (define-key map (kbd "C-c w") 'helm-org-run-refile-heading-to)
(define-key map (kbd "C-c l") 'helm-org-run-insert-link-to-heading-at-marker)
map)
"Keymap for `helm-source-org-headings-for-files'.")
@@ -204,7 +214,7 @@ nothing to CANDIDATES."
(defun helm-org--get-candidates-in-file (filename &optional fontify nofname parents)
(with-current-buffer (pcase filename
((pred bufferp) filename)
- ((pred stringp) (find-file-noselect filename)))
+ ((pred stringp) (find-file-noselect filename t)))
(let ((match-fn (if fontify
#'match-string
#'match-string-no-properties))
@@ -275,28 +285,25 @@ nothing to CANDIDATES."
(helm-exit-and-execute-action
'helm-org-insert-link-to-heading-at-marker)))
-(defun helm-org-heading-refile (marker)
- "Refile current heading or marked to MARKER.
-The current heading is the heading where cursor was
-before entering helm session, it will be used unless
-you mark a candidate, in this case helm will go to this marked
-candidate in org buffer and refile this candidate to MARKER.
-NOTE that of course if you have marked more than one candidate,
-all the subsequent candidates will be ignored."
- (let ((mkd (with-helm-buffer
- (and helm-marked-candidates
- (car (helm-marked-candidates))))))
- (save-selected-window
- (when (eq major-mode 'org-agenda-mode)
- (org-agenda-switch-to))
- (when mkd (helm-org-goto-marker mkd))
- (org-cut-subtree)
- (let ((target-level (with-current-buffer (marker-buffer marker)
- (goto-char (marker-position marker))
- (org-current-level))))
- (helm-org-goto-marker marker)
- (org-end-of-subtree t t)
- (org-paste-subtree (+ target-level 1))))))
+(defun helm-org--refile-heading-to (marker)
+ "Refile headings to heading at MARKER.
+If multiple candidates are marked in the Helm session, they will
+all be refiled. If no headings are marked, the selected heading
+will be refiled."
+ (let* ((victims (with-helm-buffer (helm-marked-candidates)))
+ (buffer (marker-buffer marker))
+ (filename (buffer-file-name buffer))
+ (rfloc (list nil filename nil marker)))
+ (when (and (= 1 (length victims))
+ (equal (helm-get-selection) (car victims)))
+ ;; No candidates are marked; we are refiling the entry at point
+ ;; to the selected heading
+ (setq victims (list (point))))
+ ;; Probably best to check that everything returned a value
+ (when (and victims buffer filename rfloc)
+ (cl-loop for victim in victims
+ do (org-with-point-at victim
+ (org-refile nil nil rfloc))))))
(defun helm-org-in-buffer-preselect ()
(if (org-on-heading-p)
@@ -305,26 +312,36 @@ all the subsequent candidates will be ignored."
(outline-previous-visible-heading 1)
(buffer-substring-no-properties (point-at-bol) (point-at-eol)))))
-(defun helm-org-run-heading-refile ()
+(defun helm-org-run-refile-heading-to ()
(interactive)
(with-helm-alive-p
- (helm-exit-and-execute-action 'helm-org-heading-refile)))
-(put 'helm-org-run-heading-refile 'helm-only t)
-
+ (helm-exit-and-execute-action 'helm-org--refile-heading-to)))
+(put 'helm-org-run-refile-heading-to 'helm-only t)
+
;;;###autoload
(defun helm-org-agenda-files-headings ()
"Preconfigured helm for org files headings."
(interactive)
- (helm :sources (helm-source-org-headings-for-files (org-agenda-files))
- :candidate-number-limit 99999
- :truncate-lines helm-org-truncate-lines
- :buffer "*helm org headings*"))
+ (let ((autosaves (cl-loop for f in (org-agenda-files)
+ when (file-exists-p
+ (expand-file-name
+ (concat "#" (helm-basename f) "#")
+ (helm-basedir f)))
+ collect (helm-basename f))))
+ (when (or (null autosaves)
+ helm-org-ignore-autosaves
+ (y-or-n-p (format "%s have auto save data, continue?"
+ (mapconcat 'identity autosaves ", "))))
+ (helm :sources (helm-source-org-headings-for-files (org-agenda-files))
+ :candidate-number-limit 99999
+ :truncate-lines helm-org-truncate-lines
+ :buffer "*helm org headings*"))))
;;;###autoload
(defun helm-org-in-buffer-headings ()
"Preconfigured helm for org buffer headings."
(interactive)
- (let (helm-org-show-filename helm-org-format-outline-path)
+ (let (helm-org-show-filename)
(helm :sources (helm-source-org-headings-for-files
(list (current-buffer)))
:candidate-number-limit 99999
@@ -354,7 +371,7 @@ current heading."
:candidate-number-limit 99999
:truncate-lines helm-org-truncate-lines
:buffer "*helm org capture templates*"))
-
+
;;; Org tag completion
;; Based on code from Anders Johansson posted on 3 Mar 2016 at
diff --git a/helm-pkg.el b/helm-pkg.el
index ada7fd0d..c7c8973e 100644
--- a/helm-pkg.el
+++ b/helm-pkg.el
@@ -1,11 +1,11 @@
;;; helm-pkg.el --- define helm for package.el
-(define-package "helm" "2.8.1"
+(define-package "helm" "2.8.2"
"Helm is an Emacs incremental and narrowing framework"
'((emacs "24.4")
(async "1.9.2")
(popup "0.5.3")
- (helm-core "2.8.1"))
+ (helm-core "2.8.2"))
:url "https://emacs-helm.github.io/helm/")
;; Local Variables:
diff --git a/helm-regexp.el b/helm-regexp.el
index 60f44b7f..33f6508e 100644
--- a/helm-regexp.el
+++ b/helm-regexp.el
@@ -320,6 +320,12 @@ Same as `helm-moccur-goto-line' but go in new frame."
(helm-exit-and-execute-action 'helm-moccur-goto-line)))
(put 'helm-moccur-run-default-action 'helm-only t)
+(defvar helm-moccur-before-init-hook nil
+ "Hook that runs before initialization of the helm buffer.")
+
+(defvar helm-moccur-after-init-hook nil
+ "Hook that runs after initialization of the helm buffer.")
+
(defvar helm-source-moccur nil)
(defclass helm-source-multi-occur (helm-source-in-buffer)
((init :initform (lambda ()
@@ -339,6 +345,8 @@ Same as `helm-moccur-goto-line' but go in new frame."
(keymap :initform helm-moccur-map)
(history :initform 'helm-occur-history)
(requires-pattern :initform 2)
+ (before-init-hook :initform 'helm-moccur-before-init-hook)
+ (after-init-hook :initform 'helm-moccur-after-init-hook)
(group :initform 'helm-regexp)))
(defun helm-moccur-resume-fn ()
diff --git a/helm-source.el b/helm-source.el
index 44d156d4..eda58636 100644
--- a/helm-source.el
+++ b/helm-source.el
@@ -669,10 +669,12 @@ inherit from `helm-source'.")
:custom function
:documentation
" A function like `buffer-substring-no-properties' or `buffer-substring'.
- This function converts point of line-beginning and point of line-end,
- which represents a candidate computed by `helm-candidates-in-buffer'.
+ This function converts region from point at line-beginning and point
+ at line-end in the `helm-candidate-buffer' to a string which will be displayed
+ in the `helm-buffer', it takes two args BEG and END.
By default, `helm-candidates-in-buffer' uses
- `buffer-substring-no-properties'.")
+ `buffer-substring-no-properties' which does no conversion and doesn't carry
+ text properties.")
(search
:initarg :search
@@ -707,8 +709,15 @@ inherit from `helm-source'.")
:MULTIMATCH slot."))
"Use this source to make helm sources storing candidates inside a buffer.
+
+The buffer storing candidates is generated by `helm-candidate-buffer' function
+and all search are done in this buffer, results are transfered to the `helm-buffer'
+when done.
Contrarily to `helm-source-sync' candidates are matched using a function
-like `re-search-forward', see below documentation of :search slot.
+like `re-search-forward' (see below documentation of `:search' slot) which makes
+the search much faster than matching candidates one by one.
+If you want to add search functions to your sources, don't use `:match' which
+will raise an error, but `:search'.
See `helm-candidates-in-buffer' for more infos.")
(defclass helm-source-dummy (helm-source)
@@ -908,9 +917,6 @@ an eieio class."
(setf (slot-value source 'header-line)
(helm-source--persistent-help-string it source))
(setf (slot-value source 'header-line) (helm-source--header-line source)))
- (helm-aif (slot-value source 'candidate-number-limit)
- (and (symbolp it) (setf (slot-value source 'candidate-number-limit)
- (symbol-value it))))
(when (and (slot-value source 'fuzzy-match) helm-fuzzy-sort-fn)
(setf (slot-value source 'filtered-candidate-transformer)
(helm-aif (slot-value source 'filtered-candidate-transformer)
diff --git a/helm-utils.el b/helm-utils.el
index 39e49e92..b0c0eb04 100644
--- a/helm-utils.el
+++ b/helm-utils.el
@@ -705,11 +705,12 @@ Assume regexp is a pcre based regexp."
(cancel-timer helm--show-help-echo-timer)
(setq helm--show-help-echo-timer nil)))
-(defun helm-show-help-echo ()
+(defun helm-maybe-show-help-echo ()
(when helm--show-help-echo-timer
(cancel-timer helm--show-help-echo-timer)
(setq helm--show-help-echo-timer nil))
(when (and helm-alive-p
+ helm-popup-tip-mode
(member (assoc-default 'name (helm-get-current-source))
helm-sources-using-help-echo-popup))
(setq helm--show-help-echo-timer
@@ -732,12 +733,10 @@ Assume regexp is a pcre based regexp."
(require 'popup)
(if helm-popup-tip-mode
(progn
- (add-hook 'helm-after-update-hook 'helm-show-help-echo) ; Needed for async sources.
- (add-hook 'helm-move-selection-after-hook 'helm-show-help-echo)
+ (add-hook 'helm-move-selection-after-hook 'helm-maybe-show-help-echo)
(add-hook 'helm-cleanup-hook 'helm-cancel-help-echo-timer))
- (remove-hook 'helm-after-update-hook 'helm-show-help-echo)
- (remove-hook 'helm-move-selection-after-hook 'helm-show-help-echo)
- (remove-hook 'helm-cleanup-hook 'helm-cancel-help-echo-timer)))
+ (remove-hook 'helm-move-selection-after-hook 'helm-maybe-show-help-echo)
+ (remove-hook 'helm-cleanup-hook 'helm-cancel-help-echo-timer)))
(defun helm-open-file-with-default-tool (file)
"Open FILE with the default tool on this platform."
diff --git a/helm.el b/helm.el
index 03faecad..accb57c5 100644
--- a/helm.el
+++ b/helm.el
@@ -812,10 +812,7 @@ This hook runs after `helm-buffer' is created but not from
(make-obsolete-variable 'helm-update-hook 'helm-after-update-hook "1.9.9")
(defvar helm-after-update-hook nil
- "Runs after updating the helm buffer with the new input pattern.
-This is very similar to `helm-update-hook' except the selection
-is not moved. Hook is useful for selecting a particular object
-instead of the first one.")
+ "Runs after updating the helm buffer with the new input pattern.")
(defvar helm-cleanup-hook nil
"Runs after exiting the minibuffer and before performing an
@@ -1200,6 +1197,9 @@ It also accepts function or variable symbol.")
(defvar helm-async-outer-limit-hook nil
"A hook that run in async sources when process output comes out of `candidate-number-limit'.
Should be set locally to `helm-buffer' with `helm-set-local-variable'.")
+
+(defvar helm-quit-hook nil
+ "A hook that run when quitting helm.")
;;; Internal Variables
;;
@@ -2197,6 +2197,7 @@ ANY-KEYMAP ANY-DEFAULT ANY-HISTORY See `helm'."
(helm-log (concat "[End session] " (make-string 41 ?-)))))
(quit
(helm-restore-position-on-quit)
+ (helm-log-run-hook 'helm-quit-hook)
(helm-log (concat "[End session (quit)] " (make-string 34 ?-)))
nil))
(when (fboundp 'advice-remove)
@@ -2446,9 +2447,29 @@ frame configuration as per `helm-save-configuration-functions'."
;; This is needed for minibuffer own-frame config
;; when recursive minibuffers are in use.
;; e.g M-: + helm-minibuffer-history.
- (let ((frame (if (minibufferp helm-current-buffer)
- (selected-frame)
- (last-nonminibuffer-frame))))
+ (cl-letf ((frame (if (minibufferp helm-current-buffer)
+ (selected-frame)
+ (last-nonminibuffer-frame)))
+ ;; This is a workaround, because the i3 window
+ ;; manager developers are refusing to fix their
+ ;; broken timestamp and event handling.
+ ;;
+ ;; We basically just disable the part of
+ ;; select-frame-set-input-focus that would call
+ ;; XSetInputFocus in Xlib (x-focus-frame), that
+ ;; resets a timestamp in the xserver which the i3
+ ;; developers fail to notice.
+ ;;
+ ;; Since they don't know about the new timestamp,
+ ;; their keyboard handling can break after a helm
+ ;; user quits emacs, as reported in #1641.
+ ;;
+ ;; Fortunately for us, we really don't need this
+ ;; XSetInputFocus call, since we already have focus
+ ;; for Emacs, the user is just using helm! We call
+ ;; select-frame-set-input-focus for the other
+ ;; side-effects, not for x-focus-frame.
+ ((symbol-function 'x-focus-frame) #'ignore))
(select-frame-set-input-focus frame))))))
(defun helm-split-window-default-fn (window)
@@ -3189,7 +3210,9 @@ e.g:
If \(candidate-number-limit\) is in SOURCE, show all candidates in SOURCE.
If \(candidate-number-limit . 123\) is in SOURCE limit candidate to 123."
(helm-aif (assq 'candidate-number-limit source)
- (or (cdr it) 99999999)
+ ;; When assoc value is nil use by default 99999999 otherwise use
+ ;; the assoc value, when it is a symbol interpret its value (#1831).
+ (or (helm-aand (cdr it) (helm-interpret-value it)) 99999999)
(or helm-candidate-number-limit 99999999)))
(defun helm-candidate-get-display (candidate)
@@ -3594,6 +3617,13 @@ It is used for narrowing list of candidates to the
(helm-while-no-input ,@body))))
(defun helm--collect-matches (src-list)
+ "Returns a list of matches for each source in SRC-LIST.
+
+The resulting value is a list of lists, e.g ((a b c) (c d) (e f)) or
+\(nil nil nil) for three sources when no matches found, however this
+function can be interrupted by new input and in this case returns a
+plain `nil' i.e not (nil), in this case `helm-update' is not rendering
+the source, keeping previous candidates in display."
(let ((matches (helm--maybe-use-while-no-input
(cl-loop for src in src-list
collect (helm-compute-matches src)))))
@@ -3669,7 +3699,7 @@ without recomputing them, it should be a list of lists."
(setq matches (or candidates (helm--collect-matches sources))))
;; If computing matches finished and is not interrupted
;; erase the helm-buffer and render results (Fix #1157).
- (when matches
+ (when matches ;; nil only when interrupted by helm-while-no-input.
(erase-buffer) ; [1]
(cl-loop for src in sources
for mtc in matches
@@ -3678,9 +3708,14 @@ without recomputing them, it should be a list of lists."
;; to avoid cursor moving upside down (issue #1703).
(helm--update-move-first-line)
(helm--reset-update-flag)))
+ ;; When there is only one async source, update mode-line and run
+ ;; `helm-after-update-hook' in `helm-output-filter--post-process',
+ ;; when there is more than one source, update mode-line and run
+ ;; `helm-after-update-hook' now even if an async source is
+ ;; present and running in BG.
(let ((src (or source (helm-get-current-source))))
(unless (assq 'candidates-process src)
- (helm-display-mode-line src)
+ (helm-display-mode-line src 'force)
(helm-log-run-hook 'helm-after-update-hook)))
(when preselect
(helm-log "Update preselect candidate %s" preselect)
@@ -3703,7 +3738,7 @@ without recomputing them, it should be a list of lists."
;; Entering repeatedly these strings (*, ?) takes 100% CPU
;; and hang emacs on MacOs preventing deleting backward those
;; characters (issue #1802).
- (not (string-match-p "[*]\\{2,\\}\\|[?]\\{3,\\}" helm-pattern))
+ (not (string-match-p "\\`[*]+\\'" helm-pattern))
;; These incomplete regexps hang helm forever
;; so defer update. Maybe replace spaces quoted when using
;; multi-match.
@@ -5880,7 +5915,8 @@ sources."
(goto-char (point-min))
(search-forward o-src-str nil t)
(while (and (search-forward o-str nil t)
- (not (overlays-at (point-at-bol 0)))
+ (cl-loop for ov in (overlays-at (point-at-bol 0))
+ never (overlay-get ov 'visible-mark))
(helm-current-source-name= o-src-str))
(setq beg (match-beginning 0)
end (match-end 0))