diff options
author | Sean Whitton <spwhitton@spwhitton.name> | 2017-12-30 14:39:21 +0000 |
---|---|---|
committer | Sean Whitton <spwhitton@spwhitton.name> | 2017-12-30 14:39:21 +0000 |
commit | 688d6135cbe7fb06b32aa5d562bcce4ff3d0dc77 (patch) | |
tree | 1b3652e29995520c1f98d514df3fa769124a0b51 | |
parent | 8be4fa3d95dddb096f05cf25fdeee780dd42e274 (diff) | |
parent | 5b2057c7755f6ea20e1ea011c6fb992d12650161 (diff) |
Merge tag 'v2.8.7'
Version 2.8.7
* New command to toggle helm buffer full-frame.
* Allow yanking marked and choosing separator from main action, the third action "append" have been removed.
* Possibility to create new kmacro from marked kmacros.
* Allow toggling mark on more than one candidate with numeric prefix args.
# gpg: Signature made Sat 23 Dec 2017 08:02:48 GMT
# gpg: using DSA key 28D17F5359F29997
# gpg: Can't check signature: No public key
-rw-r--r-- | helm-core-pkg.el | 2 | ||||
-rw-r--r-- | helm-elisp.el | 53 | ||||
-rw-r--r-- | helm-files.el | 142 | ||||
-rw-r--r-- | helm-for-files.el | 2 | ||||
-rw-r--r-- | helm-help.el | 15 | ||||
-rw-r--r-- | helm-mode.el | 20 | ||||
-rw-r--r-- | helm-pkg.el | 4 | ||||
-rw-r--r-- | helm-ring.el | 204 | ||||
-rw-r--r-- | helm-source.el | 59 | ||||
-rw-r--r-- | helm-tags.el | 9 | ||||
-rw-r--r-- | helm-utils.el | 15 | ||||
-rw-r--r-- | helm.el | 195 |
12 files changed, 442 insertions, 278 deletions
diff --git a/helm-core-pkg.el b/helm-core-pkg.el index 0cdd9302..65c39023 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.6" +(define-package "helm-core" "2.8.7" "Development files for Helm" '((emacs "24.4") (async "1.9.2")) diff --git a/helm-elisp.el b/helm-elisp.el index e6b9e6b6..6cc3fa78 100644 --- a/helm-elisp.el +++ b/helm-elisp.el @@ -150,10 +150,9 @@ fuzzy completion is not available in `completion-at-point'." (helm-get-selection))))) (defun helm-show-completion-init-overlay (beg end) - (when (and helm-turn-on-show-completion beg end) - (setq helm-show-completion-overlay (make-overlay beg end)) - (overlay-put helm-show-completion-overlay - 'face 'helm-lisp-show-completion))) + (setq helm-show-completion-overlay (make-overlay beg end)) + (overlay-put helm-show-completion-overlay + 'face 'helm-lisp-show-completion)) (defun helm-show-completion-display-function (buffer &rest _args) "A special resized helm window is used depending on position in BUFFER." @@ -179,31 +178,29 @@ fuzzy completion is not available in `completion-at-point'." BEG and END are the beginning and end position of the current completion in `helm-current-buffer'. BODY is an helm call where we want to enable show completion. -If `helm-turn-on-show-completion' is nil just do nothing." +If `helm-turn-on-show-completion' is nil do nothing." (declare (indent 2) (debug t)) - `(let ((helm-move-selection-after-hook - (and helm-turn-on-show-completion - (append (list 'helm-show-completion) - helm-move-selection-after-hook))) - (helm-always-two-windows t) - (helm-split-window-default-side - (if (eq helm-split-window-default-side 'same) - 'below helm-split-window-default-side)) - helm-split-window-inside-p - helm-reuse-last-window-split-state) - (helm-set-local-variable - 'helm-display-function - (if helm-show-completion-use-special-display - 'helm-show-completion-display-function - 'helm-default-display-buffer)) - (unwind-protect - (progn - (helm-show-completion-init-overlay ,beg ,end) - ,@body) - (when (and helm-turn-on-show-completion - helm-show-completion-overlay - (overlayp helm-show-completion-overlay)) - (delete-overlay helm-show-completion-overlay))))) + `(unwind-protect + (if helm-turn-on-show-completion + (let ((helm-move-selection-after-hook + (append (list 'helm-show-completion) + helm-move-selection-after-hook)) + (helm-split-window-default-side + (if (eq helm-split-window-default-side 'same) + 'below helm-split-window-default-side)) + helm-split-window-inside-p + helm-reuse-last-window-split-state) + (helm-set-local-variable + 'helm-display-function + (if helm-show-completion-use-special-display + 'helm-show-completion-display-function + 'helm-default-display-buffer)) + (helm-show-completion-init-overlay ,beg ,end) + ,@body) + ,@body) + (when (and helm-show-completion-overlay + (overlayp helm-show-completion-overlay)) + (delete-overlay helm-show-completion-overlay)))) ;;; Lisp symbol completion. diff --git a/helm-files.el b/helm-files.el index b6c2adc8..0a2d70e2 100644 --- a/helm-files.el +++ b/helm-files.el @@ -575,7 +575,7 @@ Should not be used among other sources.") (cl-loop for f in candidates for ff = (helm-ff-filter-candidate-one-by-one f) when ff collect ff)))) - (persistent-action :initform 'helm-find-files-persistent-action) + (persistent-action-if :initform 'helm-find-files-persistent-action-if) (persistent-help :initform "Hit1 Expand Candidate, Hit2 or (C-u) Find file") (help-message :initform 'helm-ff-help-message) (mode-line :initform (list "File(s)" helm-mode-line-string)) @@ -2471,7 +2471,7 @@ Never kill `helm-current-buffer'. Never kill buffer modified. This is called normally on third hit of \ \\<helm-map>\\[helm-execute-persistent-action] -in `helm-find-files-persistent-action'." +in `helm-find-files-persistent-action-if'." (let* ((buf (get-file-buffer candidate)) (buf-name (buffer-name buf)) (win (get-buffer-window buf)) @@ -2761,7 +2761,7 @@ This affect directly file CANDIDATE." (format "No program %s found to extract exif" helm-ff-exif-data-program))) -(cl-defun helm-find-files-persistent-action (candidate) +(cl-defun helm-find-files-persistent-action-if (candidate) "Open subtree CANDIDATE without quitting helm. If CANDIDATE is not a directory expand CANDIDATE filename. If CANDIDATE is alone, open file CANDIDATE filename. @@ -2785,66 +2785,77 @@ If a prefix arg is given or `helm-follow-mode' is on open file." (unless image-cand (when follow (helm-follow-mode -1) - (cl-return-from helm-find-files-persistent-action + (cl-return-from helm-find-files-persistent-action-if (message "Helm-follow-mode allowed only on images, disabling")))) (cond ((and (helm-ff--invalid-tramp-name-p) (string-match helm-tramp-file-name-regexp candidate)) - ;; First hit insert hostname and - ;; second hit insert ":" and expand. - (if (string= candidate helm-pattern) - (funcall insert-in-minibuffer (concat candidate ":")) - (funcall insert-in-minibuffer candidate))) + (cons (lambda (_candidate) + ;; First hit insert hostname and + ;; second hit insert ":" and expand. + (if (string= candidate helm-pattern) + (funcall insert-in-minibuffer (concat candidate ":")) + (funcall insert-in-minibuffer candidate))) + 'never-split)) (;; A symlink directory, expand it but not to its truename ;; unless a prefix arg is given. (and (file-directory-p candidate) (file-symlink-p candidate)) - (funcall insert-in-minibuffer - (file-name-as-directory - (if current-prefix-arg - (file-truename (expand-file-name candidate)) - (expand-file-name candidate))))) + (cons (lambda (_candidate) + (funcall insert-in-minibuffer + (file-name-as-directory + (if current-prefix-arg + (file-truename (expand-file-name candidate)) + (expand-file-name candidate))))) + 'never-split)) ;; A directory, open it. ((file-directory-p candidate) - (when (string= (helm-basename candidate) "..") - (setq helm-ff-last-expanded helm-ff-default-directory)) - (funcall insert-in-minibuffer (file-name-as-directory - (expand-file-name candidate)))) + (cons (lambda (_candidate) + (when (string= (helm-basename candidate) "..") + (setq helm-ff-last-expanded helm-ff-default-directory)) + (funcall insert-in-minibuffer (file-name-as-directory + (expand-file-name candidate)))) + 'never-split)) ;; A symlink file, expand to it's true name. (first hit) ((and (file-symlink-p candidate) (not current-prefix-arg) (not follow)) - (funcall insert-in-minibuffer (file-truename candidate))) + (cons (lambda (_candidate) + (funcall insert-in-minibuffer (file-truename candidate))) + 'never-split)) ;; A regular file, expand it, (first hit) ((and (>= num-lines-buf 3) (not current-prefix-arg) (not follow)) - (setq helm-pattern "") ; Force update. - (funcall insert-in-minibuffer new-pattern)) + (cons (lambda (_candidate) + (setq helm-pattern "") ; Force update. + (funcall insert-in-minibuffer new-pattern)) + 'never-split)) ;; An image file and it is the second hit on C-j, ;; show the file in `image-dired'. (image-cand - (require 'image-dired) - (let* ((win (get-buffer-window - image-dired-display-image-buffer 'visible)) - (remove-buf-only - (and win - (with-helm-buffer - (file-equal-p candidate - (with-current-buffer - image-dired-display-image-buffer - (get-text-property - (point-min) - 'original-file-name))))))) - (when remove-buf-only - (set-window-buffer win helm-current-buffer)) - (when (buffer-live-p (get-buffer image-dired-display-image-buffer)) - (kill-buffer image-dired-display-image-buffer)) - (unless remove-buf-only - ;; Fix emacs bug never fixed upstream. - (unless (file-directory-p image-dired-dir) - (make-directory image-dired-dir)) - (image-dired-display-image candidate) - (message nil) - (switch-to-buffer image-dired-display-image-buffer) - (with-current-buffer image-dired-display-image-buffer - (let ((exif-data (helm-ff-exif-data candidate))) - (setq default-directory helm-ff-default-directory) - (image-dired-update-property 'help-echo exif-data)))))) + (lambda (_candidate) + (require 'image-dired) + (let* ((win (get-buffer-window + image-dired-display-image-buffer 'visible)) + (remove-buf-only + (and win + (with-helm-buffer + (file-equal-p candidate + (with-current-buffer + image-dired-display-image-buffer + (get-text-property + (point-min) + 'original-file-name))))))) + (when remove-buf-only + (set-window-buffer win helm-current-buffer)) + (when (buffer-live-p (get-buffer image-dired-display-image-buffer)) + (kill-buffer image-dired-display-image-buffer)) + (unless remove-buf-only + ;; Fix emacs bug never fixed upstream. + (unless (file-directory-p image-dired-dir) + (make-directory image-dired-dir)) + (image-dired-display-image candidate) + (message nil) + (switch-to-buffer image-dired-display-image-buffer) + (with-current-buffer image-dired-display-image-buffer + (let ((exif-data (helm-ff-exif-data candidate))) + (setq default-directory helm-ff-default-directory) + (image-dired-update-property 'help-echo exif-data))))))) ;; Allow browsing archive on avfs fs. ;; Assume volume is already mounted with mountavfs. ((helm-aand helm-ff-avfs-directory @@ -2853,7 +2864,9 @@ If a prefix arg is given or `helm-follow-mode' is on open file." (regexp-quote (expand-file-name helm-ff-avfs-directory)) it) (helm-ff-file-compressed-p candidate)) - (funcall insert-in-minibuffer (concat candidate "#"))) + (cons (lambda (_candidate) + (funcall insert-in-minibuffer (concat candidate "#"))) + 'never-split)) ;; File doesn't exists and basename starts with ".." or " ", ;; Start a recursive search for directories. ((and (not (file-exists-p candidate)) @@ -2862,19 +2875,24 @@ If a prefix arg is given or `helm-follow-mode' is on open file." (helm-basename candidate))) ;; As soon as the final "/" is added the job is passed ;; to `helm-ff-auto-expand-to-home-or-root'. - (funcall insert-in-minibuffer (concat candidate "/"))) + (cons (lambda (_candidate) + (funcall insert-in-minibuffer (concat candidate "/"))) + 'never-split)) ;; File is not existing and have no basedir, typically when ;; user hit C-k (minibuffer is empty) and then write foo and ;; hit C-j. This make clear that when no basedir, helm will ;; create the file in default-directory. ((and (not (file-exists-p candidate)) (not (helm-basedir candidate))) - (funcall insert-in-minibuffer - (expand-file-name candidate default-directory))) + (cons (lambda (_candidate) + (funcall insert-in-minibuffer + (expand-file-name candidate default-directory))) + 'never-split)) ;; On second hit we open file. ;; On Third hit we kill it's buffer maybe. (t - (funcall helm-ff-kill-or-find-buffer-fname-fn candidate))))) + (lambda (_candidate) + (funcall helm-ff-kill-or-find-buffer-fname-fn candidate)))))) ;;; Recursive dirs completion @@ -2923,6 +2941,17 @@ If a prefix arg is given or `helm-follow-mode' is on open file." (member (file-name-extension candidate) helm-ff-file-compressed-list)) +(defun helm-ff--fname-at-point () + "Try to guess fname at point." + (let ((end (point)) + (limit (helm-aif (bounds-of-thing-at-point 'filename) + (car it) + (point)))) + (save-excursion + (while (re-search-backward "\\(~\\|/\\|[[:lower:][:upper:]]:/\\)" + limit t)) + (buffer-substring-no-properties (point) end)))) + (defun helm-insert-file-name-completion-at-point (_candidate) "Insert file name completion at point." (with-helm-current-buffer @@ -2931,9 +2960,10 @@ If a prefix arg is given or `helm-follow-mode' is on open file." (let* ((mkds (helm-marked-candidates :with-wildcard t)) (candidate (car mkds)) (end (point)) - (tap (thing-at-point 'filename)) - (guess (and (stringp tap) (substring-no-properties tap))) - (beg (- (point) (length guess))) + (tap (helm-ff--fname-at-point)) + (guess (and (stringp tap) + (substring-no-properties tap))) + (beg (if guess (- (point) (length guess)) (point))) (full-path-p (and (stringp guess) (or (string-match-p (concat "^" (getenv "HOME")) diff --git a/helm-for-files.el b/helm-for-files.el index 6c3a3550..53714052 100644 --- a/helm-for-files.el +++ b/helm-for-files.el @@ -127,7 +127,7 @@ this source is accessible and properly loaded." '(("Delete file(s) from recentf" . (lambda (_candidate) (cl-loop for file in (helm-marked-candidates) - do (setq recentf-list (delq file recentf-list))))))))) + do (setq recentf-list (delete file recentf-list))))))))) (defvar helm-source-recentf nil "See (info \"(emacs)File Conveniences\"). diff --git a/helm-help.el b/helm-help.el index 10298da7..e5c94cbe 100644 --- a/helm-help.el +++ b/helm-help.el @@ -1475,10 +1475,16 @@ Helm-kill-ring session you can navigate to next/previous line with `M-y' and It is possible to delete candidates from the kill ring. -You can concatenate marked candidates and yank them in the current buffer, thus -creating a new entry in the kill ring. See the commands below. Candidates are -concatenated with a newline as separator. Alternatively, use -`\\<helm-map>\\[helm-copy-to-buffer]' to not push a new entry in the kill ring. +You can concatenate marked candidates and yank them in the current +buffer, thus creating a new entry in the kill ring. Candidates are +concatenated with `helm-kill-ring-separator' as default but you can +change interactively the separator while yanking by using two prefix +args. When you have something else than \"\\n\" as default value for +`helm-kill-ring-separator' and you want to use \"\\n\" from prompt, use +`C-q C-j' to enter a newline in prompt. + +To not push a new entry in the kill ring, use `\\<helm-map>\\[helm-copy-to-buffer]' instead of RET +\(note that you can't change separator with this). When inserting candidates with the default action (`RET'), `point' is placed at the end of the candidate and `mark' at the beginning. You can revert this behavior @@ -1489,7 +1495,6 @@ by using a prefix argument, i.e. `C-u RET', like the regular `yank' command does \\[helm-next-line]\t\tNext line. \\[helm-previous-line]\t\tPrevious line. \\[helm-kill-ring-delete]\t\tDelete entry. -\\[helm-kill-ring-run-append]\t\tYank concatenated marked candidates. \\[helm-kill-ring-toggle-truncated]\t\tToggle truncated view of candidate. \\[helm-kill-ring-kill-selection]\t\tKill non-truncated of selection.") diff --git a/helm-mode.el b/helm-mode.el index 1b79c82b..214d073a 100644 --- a/helm-mode.el +++ b/helm-mode.el @@ -261,14 +261,16 @@ If COLLECTION is an `obarray', a TEST should be needed. See `obarray'." ;; Normally file completion should not be handled here, ;; but special cases like `find-file-at-point' do it. ;; Handle here specially such cases. - ((and (functionp collection) minibuffer-completing-file-name) + ((and (functionp collection) (not (string= input "")) + minibuffer-completing-file-name) (cl-loop for f in (funcall collection input test t) unless (member f '("./" "../")) if (string-match helm--url-regexp input) collect f else collect (concat (file-name-as-directory - (helm-basedir input)) f))) + (helm-basedir input)) + f))) ((functionp collection) (funcall collection input test t)) ((and alistp (null test)) collection) @@ -858,7 +860,7 @@ See documentation of `completing-read' and `all-completions' for details." (candidate-number-limit helm-ff-candidate-number-limit) nomark (alistp t) - (persistent-action 'helm-find-files-persistent-action) + (persistent-action-if 'helm-find-files-persistent-action-if) (persistent-help "Hit1 Expand Candidate, Hit2 or (C-u) Find file") (mode-line helm-read-file-name-mode-line-string)) "Read a file name with helm completion. @@ -892,7 +894,7 @@ Keys description: - ALISTP: Don't use `all-completions' in history (take effect only on history). -- PERSISTENT-ACTION: a persistent action function. +- PERSISTENT-ACTION-IF: a persistent if action function. - PERSISTENT-HELP: persistent help message. @@ -918,7 +920,6 @@ Keys description: (helm-ff-auto-update-initial-value (and helm-ff-auto-update-initial-value (not (minibuffer-window-active-p (minibuffer-window))))) - helm-full-frame helm-follow-mode-persistent (helm-ff-fuzzy-matching (and fuzzy @@ -956,7 +957,7 @@ Keys description: :candidates hist :nohighlight t :fuzzy-match fuzzy - :persistent-action persistent-action + :persistent-action-if persistent-action-if :persistent-help persistent-help :keymap cmap :nomark nomark @@ -988,7 +989,7 @@ Keys description: (helm-find-files-get-candidates must-match)))) :filtered-candidate-transformer 'helm-ff-sort-candidates :filter-one-by-one 'helm-ff-filter-candidate-one-by-one - :persistent-action persistent-action + :persistent-action-if persistent-action-if :persistent-help persistent-help :volatile t :keymap cmap @@ -1006,6 +1007,7 @@ Keys description: :case-fold-search case-fold :default default :buffer buffer + :full-frame nil :preselect preselect))) (or (cond ((and result (stringp result) @@ -1017,7 +1019,9 @@ Keys description: (if (listp default) (car default) default)) ((and result (listp result)) (mapcar #'expand-file-name result)) - (t result)) + ((and result (file-directory-p result)) + (file-name-as-directory (expand-file-name result))) + (result (expand-file-name result))) (helm-mode--keyboard-quit)))) (defun helm-mode--default-filename (fname dir initial) diff --git a/helm-pkg.el b/helm-pkg.el index 650086f7..092a8609 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.6" +(define-package "helm" "2.8.7" "Helm is an Emacs incremental and narrowing framework" '((emacs "24.4") (async "1.9.2") (popup "0.5.3") - (helm-core "2.8.6")) + (helm-core "2.8.7")) :url "https://emacs-helm.github.io/helm/") ;; Local Variables: diff --git a/helm-ring.el b/helm-ring.el index 79d2c017..bb8a9c10 100644 --- a/helm-ring.el +++ b/helm-ring.el @@ -46,19 +46,22 @@ will not have anymore separators between candidates." (integer :tag "Max candidate offset")) :group 'helm-ring) -(defcustom helm-register-max-offset 160 - "Max size of string register entries before truncating." - :group 'helm-ring - :type 'integer) - (defcustom helm-kill-ring-actions - '(("Yank" . helm-kill-ring-action-yank) - ("Delete" . helm-kill-ring-action-delete) - ("Append" . helm-kill-ring-append)) + '(("Yank marked" . helm-kill-ring-action-yank) + ("Delete marked" . helm-kill-ring-action-delete)) "List of actions for kill ring source." :group 'helm-ring :type '(alist :key-type string :value-type function)) +(defcustom helm-kill-ring-separator "\n" + "The separator used to separate marked candidates when yanking." + :group 'helm-ring + :type 'string) + +(defcustom helm-register-max-offset 160 + "Max size of string register entries before truncating." + :group 'helm-ring + :type 'integer) ;;; Kill ring ;; @@ -69,7 +72,6 @@ will not have anymore separators between candidates." (define-key map (kbd "M-y") 'helm-next-line) (define-key map (kbd "M-u") 'helm-previous-line) (define-key map (kbd "M-D") 'helm-kill-ring-delete) - (define-key map (kbd "C-M-w") 'helm-kill-ring-run-append) (define-key map (kbd "C-]") 'helm-kill-ring-toggle-truncated) (define-key map (kbd "C-c C-k") 'helm-kill-ring-kill-selection) map) @@ -140,60 +142,70 @@ Same as `helm-kill-selection-and-quit' called with a prefix arg." (cl-return) (helm-next-line)))) -(defun helm-kill-ring-action-yank (str) +(defun helm-kill-ring-action-yank (_str) + "Insert concatenated marked candidates in current-buffer. + +When two prefix args are given prompt to choose separator, otherwise +use `helm-kill-ring-separator' as default." + (let ((marked (helm-marked-candidates)) + (sep (if (equal helm-current-prefix-arg '(16)) + (read-string "Separator: ") + helm-kill-ring-separator))) + (helm-kill-ring-action-yank-1 + (cl-loop for c in (butlast marked) + concat (concat c sep) into str + finally return (concat str (car (last marked))))))) + +(defun helm-kill-ring-action-yank-1 (str) "Insert STR in `kill-ring' and set STR to the head. When called with a prefix arg, point and mark are exchanged without activating region. If this action is executed just after `yank', replace with STR as yanked string." - (with-helm-current-buffer - (unwind-protect - (progn - (setq kill-ring (delete str kill-ring)) - ;; Adding a `delete-selection' property - ;; to `helm-kill-ring-action' is not working - ;; because `this-command' will be `helm-maybe-exit-minibuffer', - ;; so use this workaround (Issue #1520). - (when (and (region-active-p) delete-selection-mode) - (delete-region (region-beginning) (region-end))) - (if (not (eq (helm-attr 'last-command helm-source-kill-ring) 'yank)) - (progn - ;; Ensure mark is at beginning of inserted text. - (push-mark) - ;; When yanking in a helm minibuffer we need a small - ;; delay to detect the mark in previous minibuffer. [1] - (run-with-timer - 0.01 nil - (lambda () - (insert-for-yank str) - (when helm-current-prefix-arg - ;; Same as exchange-point-and-mark but without - ;; activating region. - (goto-char (prog1 (mark t) - (set-marker (mark-marker) - (point) - helm-current-buffer))))))) + (let ((yank-fn (lambda (&optional before yank-pop) + (insert-for-yank str) + ;; Set the window start back where it was in + ;; the yank command, if possible. + (when yank-pop + (set-window-start (selected-window) yank-window-start t)) + (when (or (equal helm-current-prefix-arg '(4)) before) + ;; Same as exchange-point-and-mark but without + ;; activating region. + (goto-char (prog1 (mark t) + (set-marker (mark-marker) + (point) + helm-current-buffer))))))) + (with-helm-current-buffer + (unwind-protect + (progn + (setq kill-ring (delete str kill-ring)) + ;; Adding a `delete-selection' property + ;; to `helm-kill-ring-action' is not working + ;; because `this-command' will be `helm-maybe-exit-minibuffer', + ;; so use this workaround (Issue #1520). + (when (and (region-active-p) delete-selection-mode) + (delete-region (region-beginning) (region-end))) + (if (not (eq (helm-attr 'last-command helm-source-kill-ring) 'yank)) + (progn + ;; Ensure mark is at beginning of inserted text. + (push-mark) + ;; When yanking in a helm minibuffer we need a small + ;; delay to detect the mark in previous minibuffer. [1] + (run-with-timer 0.01 nil yank-fn)) ;; from `yank-pop' (let ((inhibit-read-only t) (before (< (point) (mark t)))) (if before (funcall (or yank-undo-function 'delete-region) (point) (mark t)) - (funcall (or yank-undo-function 'delete-region) (mark t) (point))) + (funcall (or yank-undo-function 'delete-region) (mark t) (point))) (setq yank-undo-function nil) (set-marker (mark-marker) (point) helm-current-buffer) - ;; Same as [1] - (run-with-timer 0.01 nil (lambda () (insert-for-yank str))) - ;; Set the window start back where it was in the yank command, - ;; if possible. - (set-window-start (selected-window) yank-window-start t) - (when before - ;; This is like exchange-point-and-mark, but doesn't activate the mark. - ;; It is cleaner to avoid activation, even though the command - ;; loop would deactivate the mark because we inserted text. - (goto-char (prog1 (mark t) - (set-marker (mark-marker) (point) helm-current-buffer))))))) - (kill-new str)))) + ;; Same as [1] but use the same mark and point as in + ;; the initial yank according to BEFORE even if no + ;; prefix arg is given. + (run-with-timer 0.01 nil yank-fn before 'pop)))) + (kill-new str))))) (define-obsolete-function-alias 'helm-kill-ring-action 'helm-kill-ring-action-yank "2.4.0") (defun helm-kill-ring-action-delete (_candidate) @@ -210,19 +222,6 @@ This is a command for `helm-kill-ring-map'." (with-helm-alive-p (helm-exit-and-execute-action 'helm-kill-ring-action-delete))) -(defun helm-kill-ring-append (_candidate) - "Yank concatenated marked candidates." - (let ((marked (helm-marked-candidates))) - (helm-kill-ring-action-yank - (cl-loop for cand in marked - for sep = (if (string-match "\n\\'" cand) "" "\n") - concat (concat cand sep))))) - -(defun helm-kill-ring-run-append () - "Yank concatenated marked candidates." - (interactive) - (with-helm-alive-p - (helm-exit-and-execute-action 'helm-kill-ring-append))) ;;;; <Mark ring> ;; DO NOT use these sources with other sources use @@ -481,34 +480,57 @@ Define your macros with `f3' and `f4'. See (info \"(emacs) Keyboard Macros\") for detailed infos. This command is useful when used with persistent action." (interactive) - (helm :sources - (helm-build-sync-source "Kmacro" - :candidates (lambda () - (helm-fast-remove-dups - (cons (kmacro-ring-head) - kmacro-ring) - :test 'equal)) - :multiline t - :candidate-transformer - (lambda (candidates) - (cl-loop for c in candidates collect - (propertize (help-key-description (car c) nil) - 'helm-realvalue c))) - :persistent-help "Execute kmacro" - :help-message 'helm-kmacro-help-message - :action - (helm-make-actions - "Execute kmacro (`C-u <n>' to execute <n> times)" - (lambda (candidate) - (interactive) - ;; Move candidate on top of list for next use. - (setq kmacro-ring (delete candidate kmacro-ring)) - (kmacro-push-ring) - (kmacro-split-ring-element candidate) - (kmacro-exec-ring-item - candidate helm-current-prefix-arg))) - :group 'helm-ring) - :buffer "*helm kmacro*")) + (let ((helm-quit-if-no-candidate + (lambda () (message "No kbd macro has been defined")))) + (helm :sources + (helm-build-sync-source "Kmacro" + :candidates (lambda () + (helm-fast-remove-dups + (cons (kmacro-ring-head) + kmacro-ring) + :test 'equal)) + :multiline t + :candidate-transformer + (lambda (candidates) + (cl-loop for c in candidates collect + (propertize (help-key-description (car c) nil) + 'helm-realvalue c))) + :persistent-help "Execute kmacro" + :help-message 'helm-kmacro-help-message + :action + (helm-make-actions + "Execute kmacro (`C-u <n>' to execute <n> times)" + 'helm-kbd-macro-execute + "Concat marked macros" + 'helm-kbd-macro-concat-macros + "Delete marked macros" + 'helm-kbd-macro-delete-macro) + :group 'helm-ring) + :buffer "*helm kmacro*"))) + +(defun helm-kbd-macro-execute (candidate) + ;; Move candidate on top of list for next use. + (setq kmacro-ring (delete candidate kmacro-ring)) + (kmacro-push-ring) + (kmacro-split-ring-element candidate) + (kmacro-exec-ring-item + candidate helm-current-prefix-arg)) + +(defun helm-kbd-macro-concat-macros (_candidate) + (let ((mkd (helm-marked-candidates))) + (when (cdr mkd) + (kmacro-push-ring) + (setq last-kbd-macro + (mapconcat 'identity + (cl-loop for km in mkd + collect (car km)) + ""))))) + +(defun helm-kbd-macro-delete-macro (_candidate) + (let ((mkd (helm-marked-candidates))) + (cl-loop for km in mkd + do (setq kmacro-ring (delete km kmacro-ring))) + (kmacro-pop-ring1))) (provide 'helm-ring) diff --git a/helm-source.el b/helm-source.el index da6a804a..9ceeba6f 100644 --- a/helm-source.el +++ b/helm-source.el @@ -157,8 +157,63 @@ " Can be a either a Function called with one parameter (the selected candidate) or a cons cell where first element is this same function and second element a symbol (e.g never-split) - that inform `helm-execute-persistent-action'to not split his - window to execute this persistent action.") + that inform `helm-execute-persistent-action' to not split his + window to execute this persistent action. + Example: + + (defun foo-persistent-action (candidate) + (do-something candidate)) + + :persistent-action '(foo-persistent-action . never-split) ; Don't split + or + :persistent-action 'foo-persistent-action ; Split + + When specifying :persistent-action by slot directly, foo-persistent-action + will be executed without quitting helm when hitting `C-j'. + + Note that other persistent actions can be defined using other + bindings than `C-j' by simply defining an interactive function bound + to a key in the keymap source. + The function should create a new attribute in source before calling + `helm-execute-persistent-action' on this attribute. + Example: + + (defun helm-ff-persistent-delete () + \"Delete current candidate without quitting.\" + (interactive) + (with-helm-alive-p + (helm-attrset 'quick-delete '(helm-ff-quick-delete . never-split)) + (helm-execute-persistent-action 'quick-delete))) + + This function is then bound in `helm-find-files-map'.") + + (persistent-action-if + :initarg :persistent-action-if + :initform nil + :custom function + :documentation + " Similar from persistent action but it is a function that should + return an object suitable for persistent action when called , i.e. a + function or a cons cell. + Example: + + (defun foo-persistent-action (candidate) + (cond (something + ;; Don't split helm-window. + (cons (lambda (_ignore) + (do-something candidate)) + 'no-split)) + ;; Split helm-window. + (something-else + (lambda (_ignore) + (do-something-else candidate))))) + + :persistent-action-if 'foo-persistent-action + + Here when hitting `C-j' one of the lambda's will be executed + depending on something or something-else condition, splitting or not + splitting as needed. + See `helm-find-files-persistent-action-if' definition as another example.") (persistent-help :initarg :persistent-help diff --git a/helm-tags.el b/helm-tags.el index f374fccc..9111d5b7 100644 --- a/helm-tags.el +++ b/helm-tags.el @@ -261,6 +261,7 @@ If no entry in cache, create one." (defun helm-etags-action-goto (switcher candidate) "Helm default action to jump to an etags entry in other window." (require 'etags) + (deactivate-mark t) (helm-log-run-hook 'helm-goto-line-before-hook) (let* ((split (helm-grep-split-line candidate)) (fname (cl-loop for tagf being the hash-keys of helm-etags-cache @@ -310,13 +311,7 @@ This function aggregates three sources of tag files: (str (if (region-active-p) (buffer-substring-no-properties (region-beginning) (region-end)) - ;; Use a raw syntax-table to determine tap. - ;; This may be wrong when calling etags - ;; with hff from a buffer that use - ;; a different syntax, but most of the time it - ;; should be better. - (with-syntax-table (standard-syntax-table) - (thing-at-point 'symbol))))) + (thing-at-point 'symbol)))) (if (cl-notany 'file-exists-p tag-files) (message "Error: No tag file found.\ Create with etags shell command, or visit with `find-tag' or `visit-tags-table'.") diff --git a/helm-utils.el b/helm-utils.el index ea99cb91..5d202c85 100644 --- a/helm-utils.el +++ b/helm-utils.el @@ -404,12 +404,17 @@ Default is `helm-current-buffer'." (defun helm-goto-char (loc) "Go to char, revealing if necessary." + (require 'org) ; On some old Emacs versions org may not be loaded. (goto-char loc) - (when (or (eq major-mode 'org-mode) - (and (boundp 'outline-minor-mode) - outline-minor-mode)) - (require 'org) ; On some old Emacs versions org may not be loaded. - (org-reveal))) + (let ((fn (cond ((eq major-mode 'org-mode) #'org-reveal) + ((and (boundp 'outline-minor-mode) + outline-minor-mode) + #'outline-show-subtree)))) + ;; outline may fail in some conditions e.g. with markdown enabled + ;; (issue #1919). + (condition-case nil + (and fn (funcall fn)) + (error nil)))) (defun helm-goto-line (lineno &optional noanim) "Goto LINENO opening only outline headline if needed. @@ -224,6 +224,7 @@ vectors, so don't use strings to define them." (define-key map (kbd "C-}") 'helm-narrow-window) (define-key map (kbd "C-{") 'helm-enlarge-window) (define-key map (kbd "C-c -") 'helm-swap-windows) + (define-key map (kbd "C-c _") 'helm-toggle-full-frame) (define-key map (kbd "C-c C-y") 'helm-yank-selection) (define-key map (kbd "C-c C-k") 'helm-kill-selection-and-quit) (define-key map (kbd "C-c C-i") 'helm-copy-to-buffer) @@ -1060,10 +1061,17 @@ current selected candidate only. (See bindings below.) Most Helm actions operate on marked candidates unless candidate-marking is explicitely forbidden for a specific source. -To mark/unmark a candidate, use \\[helm-toggle-visible-mark]. (See bindings below.) -To mark all visible unmarked candidates at once in current source use \\[helm-mark-all]. -To mark/unmark all candidates at once use \\[helm-toggle-all-marks]. -With a prefix argument, those bindings let you mark candidates in all sources. +- To mark/unmark a candidate, use \\[helm-toggle-visible-mark]. (See bindings below.) +With a numeric prefix arg mark ARG candidates forward, if ARG is negative +mark ARG candidates backward. + +- To mark all visible unmarked candidates at once in current source use \\[helm-mark-all]. +With a prefix argument, mark all candidates in all sources. + +- To unmark all visible marked candidates at once use \\[helm-unmark-all]. + +- To mark/unmark all candidates at once use \\[helm-toggle-all-marks]. +With a prefix argument, mark/unmark all candidates in all sources. Note: When multiple candidates are selected across different sources, only the candidates of the current source will be used when executing most actions (as @@ -1133,7 +1141,7 @@ this command will greatly improve `helm' interactivity, e.g. when quitting Helm accidentally. You can call \\<global-map>\\[helm-resume] with a prefix argument to choose -(with completion!) which session you'd like to resume. You can also cycle in +\(with completion!) which session you'd like to resume. You can also cycle in these sources with `helm-cycle-resume' (see above). ** Debugging Helm @@ -2363,6 +2371,9 @@ Don't use this directly, use instead `helm' with the keyword (orig-helm-last-frame-or-window-configuration helm-last-frame-or-window-configuration) (orig-one-window-p helm-onewindow-p)) + ;; FIXME Using helm-full-frame here allow showing the new + ;; helm-buffer in the same window as old helm-buffer, why? + (helm-set-local-variable 'helm-full-frame t) (unwind-protect (let (helm-current-position helm-current-buffer @@ -2372,12 +2383,12 @@ Don't use this directly, use instead `helm' with the keyword "*Helm*")) helm-sources helm-compiled-sources - (helm-full-frame t) (enable-recursive-minibuffers t)) (apply #'helm same-as-helm)) (with-current-buffer orig-helm-buffer (setq helm-alive-p t) ; Nested session set this to nil on exit. (setq helm-buffer orig-helm-buffer) + (setq helm-full-frame nil) (setq helm--prompt orig-helm--prompt) (setq helm--in-fuzzy orig-helm--in-fuzzy) (helm-initialize-overlays helm-buffer) @@ -3879,6 +3890,8 @@ respectively `helm-cand-num' and `helm-cur-source'." (not (zerop (length dispvalue)))) (funcall insert-function dispvalue) (setq end (point-at-eol)) + ;; Some strings may handle another keymap prop. + (remove-text-properties start end '(keymap nil)) (put-text-property start end 'read-only nil) ;; Some sources with candidates-in-buffer have already added ;; 'helm-realvalue property when creating candidate buffer. @@ -3886,10 +3899,7 @@ respectively `helm-cand-num' and `helm-cur-source'." (and realvalue (put-text-property start end 'helm-realvalue realvalue))) - (when (and map - ;; Don't overwrite mouse properties when - ;; redisplaying. - (not (get-text-property start 'keymap))) + (when map (define-key map [mouse-1] 'helm-mouse-select-candidate) (define-key map [mouse-2] 'ignore) (define-key map [mouse-3] 'helm-select-action) @@ -3897,9 +3907,10 @@ respectively `helm-cand-num' and `helm-cur-source'." start end `(mouse-face highlight keymap ,map - help-echo ,(helm-aif (get-text-property start 'help-echo) - (concat it "\nmouse-1: select candidate\nmouse-3: menu actions") - "mouse-1: select candidate\nmouse-3: menu actions")))) + help-echo ,(pcase (get-text-property start 'help-echo) + ((and it (pred stringp)) + (concat it "\nmouse-1: select candidate\nmouse-3: menu actions")) + (_ "mouse-1: select candidate\nmouse-3: menu actions"))))) (when num (put-text-property start end 'helm-cand-num num)) (when source @@ -3911,7 +3922,7 @@ respectively `helm-cand-num' and `helm-cur-source'." (start (overlay-start helm-selection-overlay)) (end (overlay-end helm-selection-overlay)) (help-echo (get-text-property start 'help-echo))) - (when (and help-echo + (when (and (stringp help-echo) (string-match "mouse-2: execute action" help-echo)) (put-text-property start end @@ -3927,9 +3938,10 @@ respectively `helm-cand-num' and `helm-cur-source'." helm-selection-point (overlay-end helm-selection-overlay) 'help-echo (helm-aif (get-text-property pos 'help-echo) - (if (string-match "mouse-1: select candidate" it) + (if (and (stringp it) + (string-match "mouse-1: select candidate" it)) (replace-match "mouse-2: execute action" t t it) - "mouse-2: execute action\nmouse-3: menu actions") + "mouse-2: execute action\nmouse-3: menu actions") "mouse-2: execute action\nmouse-3: menu actions"))))) (defun helm-mouse-select-candidate (event) @@ -5457,6 +5469,26 @@ If N is positive enlarge, if negative narrow." (helm-enlarge-window-1 1))) (put 'helm-enlarge-window 'helm-only t) +(defun helm-toggle-full-frame () + "Toggle helm-buffer full-frame view." + (interactive) + (cl-assert (null (helm-action-window)) nil "Unable to toggle full frame from action window") + (if (or helm-onewindow-p + (buffer-local-value 'helm-full-frame (get-buffer helm-buffer))) + (with-helm-window + (setq-local helm-full-frame nil) + (setq helm-onewindow-p nil) + (let ((split-window-preferred-function + helm-split-window-preferred-function)) + (switch-to-buffer helm-current-buffer) + (helm-display-buffer helm-buffer) + (select-window (minibuffer-window)))) + (with-helm-window + (delete-other-windows) + (setq-local helm-full-frame t) + (setq helm-onewindow-p t)))) +(put 'helm-toggle-full-frame 'helm-only t) + (defun helm-swap-windows () "Swap window holding `helm-buffer' with other window." (interactive) @@ -5562,11 +5594,14 @@ Possible values are 'left 'right 'below or 'above." (defun helm-initialize-persistent-action () (set (make-local-variable 'helm-persistent-action-display-window) nil)) -(cl-defun helm-execute-persistent-action - (&optional (attr 'persistent-action) split-onewindow) +(cl-defun helm-execute-persistent-action (&optional attr split-onewindow) "Perform the associated action ATTR without quitting helm. -ATTR default is 'persistent-action', but it can be anything else. +Arg ATTR default will be `persistent-action' or `persistent-action-if' +if unspecified depending on what's found in source, but it can be +anything else. In this case you have to add this new attribute to your source. +See `persistent-action' and `persistent-action-if' slot documentation +in `helm-source'. When `helm-full-frame' or SPLIT-ONEWINDOW are non-`nil', and `helm-buffer' is displayed in only one window, the helm window is @@ -5574,51 +5609,58 @@ split to display `helm-select-persistent-action-window' in other window to maintain visibility." (interactive) (with-helm-alive-p - (helm-log "executing persistent-action") - (let* ((source (helm-get-current-source)) - (selection (and source (helm-get-selection nil nil source))) - (attr-val (assoc-default attr source)) - ;; If attr value is a cons, use its car as persistent function - ;; and its car to decide if helm window should be splitted. - (fn (if (and (consp attr-val) - ;; maybe a lambda. - (not (functionp attr-val))) - (car attr-val) attr-val)) - (no-split (and (consp attr-val) - (not (functionp attr-val)) - (cdr attr-val))) - (cursor-in-echo-area t) - mode-line-in-non-selected-windows) - (when (eq fn 'ignore) - (cl-return-from helm-execute-persistent-action nil)) - (when source - (with-helm-window - (save-selected-window - (if no-split - (helm-select-persistent-action-window) + (let ((source (helm-get-current-source))) + (unless attr + (setq attr (or (car (assq 'persistent-action source)) + (car (assq 'persistent-action-if source))))) + (helm-log "executing persistent-action") + (let* ((selection (and source (helm-get-selection nil nil source))) + (attr-val (if (eq attr 'persistent-action-if) + (funcall (assoc-default attr source) selection) + (assoc-default attr source))) + ;; If attr value is a cons, use its car as persistent function + ;; and its car to decide if helm window should be splitted. + (fn (if (and (consp attr-val) + ;; maybe a lambda. + (not (functionp attr-val))) + (car attr-val) attr-val)) + (no-split (and (consp attr-val) + (not (functionp attr-val)) + (cdr attr-val))) + (cursor-in-echo-area t) + mode-line-in-non-selected-windows) + (when (and helm-onewindow-p (null no-split)) + (helm-toggle-full-frame)) + (when (eq fn 'ignore) + (cl-return-from helm-execute-persistent-action nil)) + (when source + (with-helm-window + (save-selected-window + (if no-split + (helm-select-persistent-action-window) (helm-select-persistent-action-window (or split-onewindow helm-onewindow-p))) - (helm-log "current-buffer = %S" (current-buffer)) - (let ((helm-in-persistent-action t) - (same-window-regexps '(".")) - display-buffer-function pop-up-windows pop-up-frames - special-display-regexps special-display-buffer-names) - (helm-execute-selection-action-1 - selection (or fn (helm-get-actions-from-current-source source)) t) - (unless (helm-action-window) - (helm-log-run-hook 'helm-after-persistent-action-hook))) - ;; A typical case is when a persistent action delete - ;; the buffer already displayed in - ;; `helm-persistent-action-display-window' and `helm-full-frame' - ;; is enabled, we end up with the `helm-buffer' - ;; displayed in two windows. - (when (and helm-onewindow-p - (> (length (window-list)) 1) - (equal (buffer-name - (window-buffer - helm-persistent-action-display-window)) - (helm-buffer-get))) - (delete-other-windows)))))))) + (helm-log "current-buffer = %S" (current-buffer)) + (let ((helm-in-persistent-action t) + (same-window-regexps '(".")) + display-buffer-function pop-up-windows pop-up-frames + special-display-regexps special-display-buffer-names) + (helm-execute-selection-action-1 + selection (or fn (helm-get-actions-from-current-source source)) t) + (unless (helm-action-window) + (helm-log-run-hook 'helm-after-persistent-action-hook))) + ;; A typical case is when a persistent action delete + ;; the buffer already displayed in + ;; `helm-persistent-action-display-window' and `helm-full-frame' + ;; is enabled, we end up with the `helm-buffer' + ;; displayed in two windows. + (when (and helm-onewindow-p + (> (length (window-list)) 1) + (equal (buffer-name + (window-buffer + helm-persistent-action-display-window)) + (helm-buffer-get))) + (delete-other-windows))))))))) (put 'helm-execute-persistent-action 'helm-only t) (defun helm-persistent-action-display-window (&optional split-onewindow) @@ -5730,20 +5772,29 @@ Meaning of prefix ARG is the same as in `reposition-window'." (cl-pushnew o helm-visible-mark-overlays) (push (cons source sel) helm-marked-candidates))) -(defun helm-toggle-visible-mark () - "Toggle helm visible mark at point." - (interactive) +(defun helm-toggle-visible-mark (arg) + "Toggle helm visible mark at point ARG times. +If ARG is negative toggle backward." + (interactive "p") (with-helm-alive-p (with-helm-window - (let ((nomark (assq 'nomark (helm-get-current-source)))) + (let ((nomark (assq 'nomark (helm-get-current-source))) + (next-fns (if (< arg 0) + '(helm-beginning-of-source-p . helm-previous-line) + '(helm-end-of-source-p . helm-next-line)))) (if nomark (message "Marking not allowed in this source") - (helm-aif (helm-this-visible-mark) - (helm-delete-visible-mark it) - (helm-make-visible-mark)) - (if (helm-end-of-source-p) - (helm-display-mode-line (helm-get-current-source)) - (helm-next-line))))))) + (cl-loop with n = (if (< arg 0) (* arg -1) arg) + repeat n do + (progn + (helm-aif (helm-this-visible-mark) + (helm-delete-visible-mark it) + (helm-make-visible-mark)) + (if (funcall (car next-fns)) + (progn + (helm-display-mode-line (helm-get-current-source)) + (cl-return nil)) + (funcall (cdr next-fns)))))))))) (put 'helm-toggle-visible-mark 'helm-only t) (defun helm-file-completion-source-p (&optional source) |