X-Git-Url: https://code.delx.au/gnu-emacs-elpa/blobdiff_plain/a7b8100b52925d82aa1d6fd8606a537ea88b91b9..a727eabe76a6b724d347f500f76968ed89457f75:/packages/swiper/counsel.el diff --git a/packages/swiper/counsel.el b/packages/swiper/counsel.el index 94c8608ee..22dc505b3 100644 --- a/packages/swiper/counsel.el +++ b/packages/swiper/counsel.el @@ -33,6 +33,13 @@ ;;; Code: (require 'swiper) +(require 'etags) + +(defvar counsel-completion-beg nil + "Completion bounds start.") + +(defvar counsel-completion-end nil + "Completion bounds end.") ;;;###autoload (defun counsel-el () @@ -75,24 +82,73 @@ :initial-input str :action #'counsel--el-action))) -(defvar counsel-completion-beg nil - "Completion bounds start.") +(declare-function slime-symbol-start-pos "ext:slime") +(declare-function slime-symbol-end-pos "ext:slime") +(declare-function slime-contextual-completions "ext:slime-c-p-c") -(defvar counsel-completion-end nil - "Completion bounds end.") +;;;###autoload +(defun counsel-cl () + "Common Lisp completion at point." + (interactive) + (setq counsel-completion-beg (slime-symbol-start-pos)) + (setq counsel-completion-end (slime-symbol-end-pos)) + (ivy-read "Symbol name: " + (car (slime-contextual-completions + counsel-completion-beg + counsel-completion-end)) + :action #'counsel--el-action)) (defun counsel--el-action (symbol) "Insert SYMBOL, erasing the previous one." (when (stringp symbol) - (when counsel-completion-beg - (delete-region - counsel-completion-beg - counsel-completion-end)) - (setq counsel-completion-beg - (move-marker (make-marker) (point))) - (insert symbol) - (setq counsel-completion-end - (move-marker (make-marker) (point))))) + (with-ivy-window + (when counsel-completion-beg + (delete-region + counsel-completion-beg + counsel-completion-end)) + (setq counsel-completion-beg + (move-marker (make-marker) (point))) + (insert symbol) + (setq counsel-completion-end + (move-marker (make-marker) (point)))))) + +(declare-function deferred:sync! "ext:deferred") +(declare-function jedi:complete-request "ext:jedi-core") +(declare-function jedi:ac-direct-matches "ext:jedi") + +(defun counsel-jedi () + "Python completion at point." + (interactive) + (let ((bnd (bounds-of-thing-at-point 'symbol))) + (if bnd + (progn + (setq counsel-completion-beg (car bnd)) + (setq counsel-completion-end (cdr bnd))) + (setq counsel-completion-beg nil) + (setq counsel-completion-end nil))) + (deferred:sync! + (jedi:complete-request)) + (ivy-read "Symbol name: " (jedi:ac-direct-matches) + :action #'counsel--py-action)) + +(defun counsel--py-action (symbol) + "Insert SYMBOL, erasing the previous one." + (when (stringp symbol) + (with-ivy-window + (when counsel-completion-beg + (delete-region + counsel-completion-beg + counsel-completion-end)) + (setq counsel-completion-beg + (move-marker (make-marker) (point))) + (insert symbol) + (setq counsel-completion-end + (move-marker (make-marker) (point))) + (when (equal (get-text-property 0 'symbol symbol) "f") + (insert "()") + (setq counsel-completion-end + (move-marker (make-marker) (point))) + (backward-char 1))))) (defvar counsel-describe-map (let ((map (make-sparse-keymap))) @@ -114,6 +170,7 @@ (defun counsel--find-symbol (x) "Find symbol definition that corresponds to string X." + (ring-insert find-tag-marker-ring (point-marker)) (let ((full-name (get-text-property 0 'full-name x))) (if full-name (find-library full-name) @@ -138,10 +195,20 @@ "Return current symbol at point as a string." (let ((s (thing-at-point 'symbol))) (and (stringp s) - (if (string-match "\\`[`']?\\(.*\\)'?\\'" s) + (if (string-match "\\`[`']?\\(.*?\\)'?\\'" s) (match-string 1 s) s)))) +(defun counsel-variable-list () + "Return the list of all currently bound variables." + (let (cands) + (mapatoms + (lambda (vv) + (when (or (get vv 'variable-documentation) + (and (boundp vv) (not (keywordp vv)))) + (push (symbol-name vv) cands)))) + cands)) + ;;;###autoload (defun counsel-describe-variable () "Forward to `describe-variable'." @@ -149,13 +216,7 @@ (let ((enable-recursive-minibuffers t)) (ivy-read "Describe variable: " - (let (cands) - (mapatoms - (lambda (vv) - (when (or (get vv 'variable-documentation) - (and (boundp vv) (not (keywordp vv)))) - (push (symbol-name vv) cands)))) - cands) + (counsel-variable-list) :keymap counsel-describe-map :preselect (counsel-symbol-at-point) :history 'counsel-describe-symbol-history @@ -165,6 +226,16 @@ (describe-variable (intern x)))))) +(ivy-set-actions + 'counsel-describe-variable + '(("i" counsel-info-lookup-symbol "info") + ("d" counsel--find-symbol "definition"))) + +(ivy-set-actions + 'counsel-describe-function + '(("i" counsel-info-lookup-symbol "info") + ("d" counsel--find-symbol "definition"))) + ;;;###autoload (defun counsel-describe-function () "Forward to `describe-function'." @@ -216,11 +287,16 @@ (require 'info-look) (info-lookup 'symbol symbol mode)) +(defvar counsel-unicode-char-history nil + "History for `counsel-unicode-char'.") + ;;;###autoload (defun counsel-unicode-char () "Insert a Unicode character at point." (interactive) (let ((minibuffer-allow-text-properties t)) + (setq counsel-completion-beg (point)) + (setq counsel-completion-end (point)) (ivy-read "Unicode name: " (mapcar (lambda (x) (propertize @@ -228,7 +304,12 @@ 'result (cdr x))) (ucs-names)) :action (lambda (char) - (insert-char (get-text-property 0 'result char)))))) + (with-ivy-window + (delete-region counsel-completion-beg counsel-completion-end) + (setq counsel-completion-beg (point)) + (insert-char (get-text-property 0 'result char)) + (setq counsel-completion-end (point)))) + :history 'counsel-unicode-char-history))) (declare-function cider-sync-request:complete "ext:cider-client") ;;;###autoload @@ -262,24 +343,21 @@ (defvar counsel--git-grep-count nil "Store the line count in current repository.") +(defun counsel-more-chars (n) + "Return two fake candidates prompting for at least N input." + (list "" + (format "%d chars more" (- n (length ivy-text))))) + (defun counsel-git-grep-function (string &optional _pred &rest _unused) "Grep in the current git repository for STRING." (if (and (> counsel--git-grep-count 20000) (< (length string) 3)) - (progn - (setq ivy--full-length counsel--git-grep-count) - (list "" - (format "%d chars more" (- 3 (length ivy-text))))) + (counsel-more-chars 3) (let* ((default-directory counsel--git-grep-dir) (cmd (format "git --no-pager grep --full-name -n --no-color -i -e %S" - (ivy--regex string t))) - res) + (setq ivy--old-re (ivy--regex string t))))) (if (<= counsel--git-grep-count 20000) - (progn - (setq res (shell-command-to-string cmd)) - (setq ivy--full-length nil) - (split-string res "\n" t)) - (setq ivy--full-length -1) + (split-string (shell-command-to-string cmd) "\n" t) (counsel--gg-candidates (ivy--regex string)) nil)))) @@ -290,40 +368,40 @@ (defun counsel-git-grep-recenter () (interactive) - (with-selected-window (ivy-state-window ivy-last) + (with-ivy-window (counsel-git-grep-action ivy--current) (recenter-top-bottom))) (defun counsel-git-grep-action (x) (when (string-match "\\`\\(.*?\\):\\([0-9]+\\):\\(.*\\)\\'" x) - (let ((file-name (match-string-no-properties 1 x)) - (line-number (match-string-no-properties 2 x))) - (find-file (expand-file-name file-name counsel--git-grep-dir)) - (goto-char (point-min)) - (forward-line (1- (string-to-number line-number))) - (re-search-forward (ivy--regex ivy-text t) (line-end-position) t) - (unless (eq ivy-exit 'done) - (setq swiper--window (selected-window)) - (swiper--cleanup) - (swiper--add-overlays (ivy--regex ivy-text)))))) + (with-ivy-window + (let ((file-name (match-string-no-properties 1 x)) + (line-number (match-string-no-properties 2 x))) + (find-file (expand-file-name file-name counsel--git-grep-dir)) + (goto-char (point-min)) + (forward-line (1- (string-to-number line-number))) + (re-search-forward (ivy--regex ivy-text t) (line-end-position) t) + (unless (eq ivy-exit 'done) + (swiper--cleanup) + (swiper--add-overlays (ivy--regex ivy-text))))))) (defvar counsel-git-grep-history nil "History for `counsel-git-grep'.") ;;;###autoload (defun counsel-git-grep (&optional initial-input) - "Grep for a string in the current git repository." + "Grep for a string in the current git repository. +INITIAL-INPUT can be given as the initial minibuffer input." (interactive) (setq counsel--git-grep-dir (locate-dominating-file default-directory ".git")) (if (null counsel--git-grep-dir) (error "Not in a git repository") (setq counsel--git-grep-count (counsel--gg-count "" t)) - (ivy-read "pattern: " 'counsel-git-grep-function + (ivy-read "git grep: " 'counsel-git-grep-function :initial-input initial-input :matcher #'counsel-git-grep-matcher - :dynamic-collection (when (> counsel--git-grep-count 20000) - 'counsel-git-grep-function) + :dynamic-collection (> counsel--git-grep-count 20000) :keymap counsel-git-grep-map :action #'counsel-git-grep-action :unwind #'swiper--cleanup @@ -336,6 +414,8 @@ (declare-function ffap-guesser "ffap") +(defvar counsel-find-file-map (make-sparse-keymap)) + ;;;###autoload (defun counsel-find-file () "Forward to `find-file'." @@ -344,12 +424,14 @@ :matcher #'counsel--find-file-matcher :action (lambda (x) - (find-file (expand-file-name x ivy--directory))) + (with-ivy-window + (find-file (expand-file-name x ivy--directory)))) :preselect (when counsel-find-file-at-point (require 'ffap) (ffap-guesser)) :require-match 'confirm-after-completion - :history 'file-name-history)) + :history 'file-name-history + :keymap counsel-find-file-map)) (defcustom counsel-find-file-ignore-regexp nil "A regexp of files to ignore while in `counsel-find-file'. @@ -397,13 +479,6 @@ Skip some dotfiles unless `ivy-text' requires them." candidates)) (setq ivy--old-re regexp)))) -(defun counsel-locate-function (str &rest _u) - (if (< (length str) 3) - (list "" - (format "%d chars more" (- 3 (length ivy-text)))) - (counsel--async-command - (concat "locate -i --regex " (ivy--regex str))))) - (defun counsel--async-command (cmd) (let* ((counsel--process " *counsel*") (proc (get-process counsel--process)) @@ -422,22 +497,75 @@ Skip some dotfiles unless `ivy-text' requires them." (if (string= event "finished\n") (progn (with-current-buffer (process-buffer process) - (setq ivy--all-candidates (split-string (buffer-string) "\n" t)) + (setq ivy--all-candidates + (ivy--sort-maybe + (split-string (buffer-string) "\n" t))) (setq ivy--old-cands ivy--all-candidates)) - (ivy--insert-minibuffer - (ivy--format ivy--all-candidates))) + (ivy--exhibit)) (if (string= event "exited abnormally with code 1\n") - (message "Error")))) + (progn + (setq ivy--all-candidates '("Error")) + (setq ivy--old-cands ivy--all-candidates) + (ivy--exhibit))))) + +(defun counsel-locate-action-extern (x) + "Use xdg-open shell command on X." + (call-process shell-file-name nil + nil nil + shell-command-switch + (format "%s %s" + (if (eq system-type 'darwin) + "open" + "xdg-open") + (shell-quote-argument x)))) + +(declare-function dired-jump "dired-x") +(defun counsel-locate-action-dired (x) + "Use `dired-jump' on X." + (dired-jump nil x)) + +(defvar counsel-locate-history nil + "History for `counsel-locate'.") + +(defcustom counsel-locate-options (if (eq system-type 'darwin) + '("-i") + '("-i" "--regex")) + "Command line options for `locate`." + :group 'ivy + :type '(repeat string)) + +(ivy-set-actions + 'counsel-locate + '(("x" counsel-locate-action-extern "xdg-open") + ("d" counsel-locate-action-dired "dired"))) + +(defun counsel-unquote-regex-parens (str) + (replace-regexp-in-string + "\\\\)" ")" + (replace-regexp-in-string + "\\\\(" "(" + str))) + +(defun counsel-locate-function (str &rest _u) + (if (< (length str) 3) + (counsel-more-chars 3) + (counsel--async-command + (format "locate %s '%s'" + (mapconcat #'identity counsel-locate-options " ") + (counsel-unquote-regex-parens + (ivy--regex str)))) + '("" "working..."))) ;;;###autoload (defun counsel-locate () - "Call locate." + "Call locate shell command." (interactive) - (ivy-read "pattern: " nil - :dynamic-collection #'counsel-locate-function - :action (lambda (val) - (when val - (find-file val))))) + (ivy-read "Locate: " #'counsel-locate-function + :dynamic-collection t + :history 'counsel-locate-history + :action (lambda (file) + (when file + (find-file file))))) (defun counsel--generic (completion-fn) "Complete thing at point with COMPLETION-FN." @@ -517,8 +645,12 @@ The libraries are offered from `load-path'." (get-text-property 0 'full-name x))) :keymap counsel-describe-map))) +(defvar counsel-gg-state nil + "The current state of candidates / count sync.") + (defun counsel--gg-candidates (regex) "Return git grep candidates for REGEX." + (setq counsel-gg-state -2) (counsel--gg-count regex) (let* ((default-directory counsel--git-grep-dir) (counsel-gg-process " *counsel-gg*") @@ -543,11 +675,13 @@ The libraries are offered from `load-path'." (with-current-buffer (process-buffer process) (setq ivy--all-candidates (split-string (buffer-string) "\n" t)) (setq ivy--old-cands ivy--all-candidates)) - (unless (eq ivy--full-length -1) - (ivy--insert-minibuffer - (ivy--format ivy--all-candidates)))) + (when (= 0 (cl-incf counsel-gg-state)) + (ivy--exhibit))) (if (string= event "exited abnormally with code 1\n") - (message "Error")))) + (progn + (setq ivy--all-candidates '("Error")) + (setq ivy--old-cands ivy--all-candidates) + (ivy--exhibit))))) (defun counsel--gg-count (regex &optional no-async) "Quickly and asynchronously count the amount of git grep REGEX matches. @@ -574,8 +708,8 @@ When NO-ASYNC is non-nil, do it synchronously." (when (string= event "finished\n") (with-current-buffer (process-buffer process) (setq ivy--full-length (string-to-number (buffer-string)))) - (ivy--insert-minibuffer - (ivy--format ivy--all-candidates))))))))) + (when (= 0 (cl-incf counsel-gg-state)) + (ivy--exhibit))))))))) (defun counsel--M-x-transformer (cmd) "Add a binding to CMD if it's bound in the current window. @@ -593,7 +727,6 @@ CMD is a command name." (declare-function smex-detect-new-commands "ext:smex") (declare-function smex-update "ext:smex") (declare-function smex-rank "ext:smex") -(declare-function package-installed-p "package") ;;;###autoload (defun counsel-M-x (&optional initial-input) @@ -608,14 +741,12 @@ Optional INITIAL-INPUT is the initial input in the minibuffer." (lambda (cands) (funcall store - (with-selected-window (ivy-state-window ivy-last) + (with-ivy-window (mapcar #'counsel--M-x-transformer cands))))) (cands obarray) (pred 'commandp) (sort t)) - (when (or (featurep 'smex) - (package-installed-p 'smex)) - (require 'smex) + (when (require 'smex nil 'noerror) (unless smex-initialized-p (smex-initialize)) (smex-detect-new-commands) @@ -660,6 +791,297 @@ Usable with `ivy-resume', `ivy-next-line-and-call' and (custom-available-themes)) :action #'counsel--load-theme-action)) +(defvar rhythmbox-library) +(declare-function rhythmbox-load-library "ext:helm-rhythmbox") +(declare-function dbus-call-method "dbus") +(declare-function rhythmbox-song-uri "ext:helm-rhythmbox") +(declare-function helm-rhythmbox-candidates "ext:helm-rhythmbox") + +(defun counsel-rhythmbox-enqueue-song (song) + "Let Rhythmbox enqueue SONG." + (let ((service "org.gnome.Rhythmbox3") + (path "/org/gnome/Rhythmbox3/PlayQueue") + (interface "org.gnome.Rhythmbox3.PlayQueue")) + (dbus-call-method :session service path interface + "AddToQueue" (rhythmbox-song-uri song)))) + +(defvar counsel-rhythmbox-history nil + "History for `counsel-rhythmbox'.") + +;;;###autoload +(defun counsel-rhythmbox () + "Choose a song from the Rhythmbox library to play or enqueue." + (interactive) + (unless (require 'helm-rhythmbox nil t) + (error "Please install `helm-rhythmbox'")) + (unless rhythmbox-library + (rhythmbox-load-library) + (while (null rhythmbox-library) + (sit-for 0.1))) + (ivy-read "Rhythmbox: " + (helm-rhythmbox-candidates) + :history 'counsel-rhythmbox-history + :action + '(1 + ("p" helm-rhythmbox-play-song "Play song") + ("e" counsel-rhythmbox-enqueue-song "Enqueue song")))) + +(defvar counsel-org-tags nil + "Store the current list of tags.") + +(defvar org-outline-regexp) +(defvar org-indent-mode) +(defvar org-indent-indentation-per-level) +(defvar org-tags-column) +(declare-function org-get-tags-string "org") +(declare-function org-move-to-column "org") + +(defun counsel-org-change-tags (tags) + (let ((current (org-get-tags-string)) + (col (current-column)) + level) + ;; Insert new tags at the correct column + (beginning-of-line 1) + (setq level (or (and (looking-at org-outline-regexp) + (- (match-end 0) (point) 1)) + 1)) + (cond + ((and (equal current "") (equal tags ""))) + ((re-search-forward + (concat "\\([ \t]*" (regexp-quote current) "\\)[ \t]*$") + (point-at-eol) t) + (if (equal tags "") + (delete-region + (match-beginning 0) + (match-end 0)) + (goto-char (match-beginning 0)) + (let* ((c0 (current-column)) + ;; compute offset for the case of org-indent-mode active + (di (if (bound-and-true-p org-indent-mode) + (* (1- org-indent-indentation-per-level) (1- level)) + 0)) + (p0 (if (equal (char-before) ?*) (1+ (point)) (point))) + (tc (+ org-tags-column (if (> org-tags-column 0) (- di) di))) + (c1 (max (1+ c0) (if (> tc 0) tc (- (- tc) (string-width tags))))) + (rpl (concat (make-string (max 0 (- c1 c0)) ?\ ) tags))) + (replace-match rpl t t) + (and c0 indent-tabs-mode (tabify p0 (point))) + tags))) + (t (error "Tags alignment failed"))) + (org-move-to-column col))) + +(defun counsel-org--set-tags () + (counsel-org-change-tags + (if counsel-org-tags + (format ":%s:" + (mapconcat #'identity counsel-org-tags ":")) + ""))) + +(defvar org-agenda-bulk-marked-entries) + +(declare-function org-get-at-bol "org") +(declare-function org-agenda-error "org-agenda") + +(defun counsel-org-tag-action (x) + (if (member x counsel-org-tags) + (progn + (setq counsel-org-tags (delete x counsel-org-tags))) + (unless (equal x "") + (setq counsel-org-tags (append counsel-org-tags (list x))) + (unless (member x ivy--all-candidates) + (setq ivy--all-candidates (append ivy--all-candidates (list x)))))) + (let ((prompt (counsel-org-tag-prompt))) + (setf (ivy-state-prompt ivy-last) prompt) + (setq ivy--prompt (concat "%-4d " prompt))) + (cond ((memq this-command '(ivy-done + ivy-alt-done + ivy-immediate-done)) + (if (eq major-mode 'org-agenda-mode) + (if (null org-agenda-bulk-marked-entries) + (let ((hdmarker (or (org-get-at-bol 'org-hd-marker) + (org-agenda-error)))) + (with-current-buffer (marker-buffer hdmarker) + (goto-char hdmarker) + (counsel-org--set-tags))) + (let ((add-tags (copy-sequence counsel-org-tags))) + (dolist (m org-agenda-bulk-marked-entries) + (with-current-buffer (marker-buffer m) + (save-excursion + (goto-char m) + (setq counsel-org-tags + (delete-dups + (append (split-string (org-get-tags-string) ":" t) + add-tags))) + (counsel-org--set-tags)))))) + (counsel-org--set-tags))) + ((eq this-command 'ivy-call) + (delete-minibuffer-contents)))) + +(defun counsel-org-tag-prompt () + (format "Tags (%s): " + (mapconcat #'identity counsel-org-tags ", "))) + +(defvar org-setting-tags) +(defvar org-last-tags-completion-table) +(defvar org-tag-persistent-alist) +(defvar org-tag-alist) +(defvar org-complete-tags-always-offer-all-agenda-tags) + +(declare-function org-at-heading-p "org") +(declare-function org-back-to-heading "org") +(declare-function org-get-buffer-tags "org") +(declare-function org-global-tags-completion-table "org") +(declare-function org-agenda-files "org") +(declare-function org-agenda-set-tags "org-agenda") + +;;;###autoload +(defun counsel-org-tag () + "Add or remove tags in org-mode." + (interactive) + (save-excursion + (if (eq major-mode 'org-agenda-mode) + (if org-agenda-bulk-marked-entries + (setq counsel-org-tags nil) + (let ((hdmarker (or (org-get-at-bol 'org-hd-marker) + (org-agenda-error)))) + (with-current-buffer (marker-buffer hdmarker) + (goto-char hdmarker) + (setq counsel-org-tags + (split-string (org-get-tags-string) ":" t))))) + (unless (org-at-heading-p) + (org-back-to-heading t)) + (setq counsel-org-tags (split-string (org-get-tags-string) ":" t))) + (let ((org-setting-tags t) + (org-last-tags-completion-table + (append org-tag-persistent-alist + (or org-tag-alist (org-get-buffer-tags)) + (and + (or org-complete-tags-always-offer-all-agenda-tags + (eq major-mode 'org-agenda-mode)) + (org-global-tags-completion-table + (org-agenda-files)))))) + (ivy-read (counsel-org-tag-prompt) + (lambda (str &rest _unused) + (delete-dups + (all-completions str 'org-tags-completion-function))) + :history 'org-tags-history + :action 'counsel-org-tag-action)))) + +;;;###autoload +(defun counsel-org-tag-agenda () + "Set tags for the current agenda item." + (interactive) + (let ((store (symbol-function 'org-set-tags))) + (unwind-protect + (progn + (fset 'org-set-tags + (symbol-function 'counsel-org-tag)) + (org-agenda-set-tags nil nil)) + (fset 'org-set-tags store)))) + +(defun counsel-ag-function (string &optional _pred &rest _unused) + "Grep in the current directory for STRING." + (if (< (length string) 3) + (counsel-more-chars 3) + (let ((regex (counsel-unquote-regex-parens + (setq ivy--old-re + (ivy--regex string))))) + (counsel--async-command + (format "ag --noheading --nocolor %S" regex)) + nil))) + +(defun counsel-ag (&optional initial-input) + "Grep for a string in the current directory using ag. +INITIAL-INPUT can be given as the initial minibuffer input." + (interactive) + (setq counsel--git-grep-dir default-directory) + (ivy-read "ag: " 'counsel-ag-function + :initial-input initial-input + :dynamic-collection t + :history 'counsel-git-grep-history + :action #'counsel-git-grep-action + :unwind #'swiper--cleanup)) + +(defun counsel-recoll-function (string &optional _pred &rest _unused) + "Grep in the current directory for STRING." + (if (< (length string) 3) + (counsel-more-chars 3) + (counsel--async-command + (format "recoll -t -b '%s'" string)) + nil)) + +;; This command uses the recollq command line tool that comes together +;; with the recoll (the document indexing database) source: +;; http://www.lesbonscomptes.com/recoll/download.html +;; You need to build it yourself (together with recoll): +;; cd ./query && make && sudo cp recollq /usr/local/bin +;; You can try the GUI version of recoll with: +;; sudo apt-get install recoll +;; Unfortunately, that does not install recollq. +(defun counsel-recoll (&optional initial-input) + "Search for a string in the recoll database. +You'll be given a list of files that match. +Selecting a file will launch `swiper' for that file. +INITIAL-INPUT can be given as the initial minibuffer input." + (interactive) + (ivy-read "recoll: " 'counsel-recoll-function + :initial-input initial-input + :dynamic-collection t + :history 'counsel-git-grep-history + :action (lambda (x) + (when (string-match "file://\\(.*\\)\\'" x) + (let ((file-name (match-string 1 x))) + (find-file file-name) + (unless (string-match "pdf$" x) + (swiper ivy-text))))))) + +(defcustom counsel-yank-pop-truncate nil + "When non-nil, truncate the display of long strings." + :group 'ivy) + +;;;###autoload +(defun counsel-yank-pop () + "Ivy replacement for `yank-pop'." + (interactive) + (if (eq last-command 'yank) + (progn + (setq counsel-completion-end (point)) + (setq counsel-completion-beg + (save-excursion + (search-backward (car kill-ring)) + (point)))) + (setq counsel-completion-beg (point)) + (setq counsel-completion-end (point))) + (let ((candidates (cl-remove-if + (lambda (s) + (or (< (length s) 3) + (string-match "\\`[\n[:blank:]]+\\'" s))) + (delete-dups kill-ring)))) + (when counsel-yank-pop-truncate + (setq candidates + (mapcar (lambda (s) + (if (string-match "\\`\\(.*\n.*\n.*\n.*\\)\n" s) + (progn + (let ((s (copy-sequence s))) + (put-text-property + (match-end 1) + (length s) + 'display + " [...]" + s) + s)) + s)) + candidates))) + (ivy-read "kill-ring: " candidates + :action 'counsel-yank-pop-action))) + +(defun counsel-yank-pop-action (s) + "Insert S into the buffer, overwriting the previous yank." + (with-ivy-window + (delete-region counsel-completion-beg + counsel-completion-end) + (insert (substring-no-properties s)) + (setq counsel-completion-end (point)))) (provide 'counsel)