branch: master commit 22139aea022e817c98de331919bbe60cbca54f52 Merge: cf04c93 3b78e0e Author: Oleh Krehel <ohwoeo...@gmail.com> Commit: Oleh Krehel <ohwoeo...@gmail.com>
Merge commit '3b78e0e503f4763f8a2d77eeacfc91213ec5532e' from swiper --- packages/swiper/Makefile | 2 +- packages/swiper/counsel.el | 106 ++++++++++++++++++++++++++++++++++--- packages/swiper/ivy.el | 128 ++++++++++++++++++++++++++++++-------------- packages/swiper/swiper.el | 23 ++++++--- 4 files changed, 203 insertions(+), 56 deletions(-) diff --git a/packages/swiper/Makefile b/packages/swiper/Makefile index 453f709..7c9910e 100644 --- a/packages/swiper/Makefile +++ b/packages/swiper/Makefile @@ -10,7 +10,7 @@ test: $(emacs) -batch $(LOAD) -l ivy-test.el -f ert-run-tests-batch-and-exit compile: - $(emacs) -batch $(LOAD) --eval "(mapc #'byte-compile-file '(\"ivy.el\" \"swiper.el\"))" + $(emacs) -batch $(LOAD) --eval "(mapc #'byte-compile-file '(\"ivy.el\" \"swiper.el\" \"counsel.el\"))" clean: rm -f *.elc diff --git a/packages/swiper/counsel.el b/packages/swiper/counsel.el index 28d10bd..fc62ade 100644 --- a/packages/swiper/counsel.el +++ b/packages/swiper/counsel.el @@ -1,8 +1,12 @@ -;;; consel.el --- Elisp completion at point -*- lexical-binding: t -*- +;;; counsel.el --- Various completion functions using Ivy -*- lexical-binding: t -*- ;; Copyright (C) 2015 Free Software Foundation, Inc. ;; Author: Oleh Krehel <ohwoeo...@gmail.com> +;; URL: https://github.com/abo-abo/swiper +;; Version: 0.1.0 +;; Package-Requires: ((emacs "24.1") (ivy "0.2.1")) +;; Keywords: completion, matching ;; This file is part of GNU Emacs. @@ -21,29 +25,117 @@ ;;; Commentary: ;; -;; Just call `counsel' to start completing the `obarray'. -;; The initial (optional) input is thing-at-point. +;; Just call one of the interactive functions in this file to complete +;; the corresponding thing using `ivy'. +;; +;; Currently available: Elisp symbols, Clojure symbols, Git files. ;;; Code: (require 'ivy) -(defun counsel () +(defun counsel-el () "Elisp completion at point." (interactive) (counsel--generic (lambda (str) (all-completions str obarray)))) -(defun couns-clj () +(defun counsel-describe-variable (variable &optional buffer frame) + "Forward to (`describe-variable' VARIABLE BUFFER FRAME)." + (interactive + (let ((v (variable-at-point)) + (enable-recursive-minibuffers t) + (preselect (thing-at-point 'symbol)) + val) + (setq val (ivy-read + (if (symbolp v) + (format + "Describe variable (default %s): " v) + "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) + nil nil preselect)) + (list (if (equal val "") + v + (intern val))))) + (describe-variable variable buffer frame)) + +(defun counsel-describe-function (function) + "Forward to (`describe-function' FUNCTION) with ivy completion." + (interactive + (let ((fn (function-called-at-point)) + (enable-recursive-minibuffers t) + (preselect (thing-at-point 'symbol)) + val) + (setq val (ivy-read (if fn + (format "Describe function (default %s): " fn) + "Describe function: ") + (let (cands) + (mapatoms + (lambda (x) + (when (fboundp x) + (push (symbol-name x) cands)))) + cands) + nil nil preselect)) + (list (if (equal val "") + fn (intern val))))) + (describe-function function)) + +(defvar info-lookup-mode) +(declare-function info-lookup->completions "info-look") +(declare-function info-lookup->mode-value "info-look") +(declare-function info-lookup-select-mode "info-look") +(declare-function info-lookup-change-mode "info-look") +(declare-function info-lookup "info-look") + +(defun counsel-info-lookup-symbol (symbol &optional mode) + "Forward to (`info-describe-symbol' SYMBOL MODE) with ivy completion." + (interactive + (progn + (require 'info-look) + (let* ((topic 'symbol) + (mode (cond (current-prefix-arg + (info-lookup-change-mode topic)) + ((info-lookup->mode-value + topic (info-lookup-select-mode)) + info-lookup-mode) + ((info-lookup-change-mode topic)))) + (completions (info-lookup->completions topic mode)) + (enable-recursive-minibuffers t) + (value (ivy-read + "Describe symbol: " + (mapcar #'car completions)))) + (list value info-lookup-mode)))) + (info-lookup 'symbol symbol mode)) + +(defun counsel-unicode-char () + "Insert a Unicode character at point." + (interactive) + (let* ((minibuffer-allow-text-properties t) + (char (ivy-read "Unicode name: " + (mapcar (lambda (x) + (propertize + (format "% -60s%c" (car x) (cdr x)) + 'result (cdr x))) + (ucs-names))))) + (insert-char (get-text-property 0 'result char)))) + +(defun counsel-clj () "Clojure completion at point." (interactive) (counsel--generic (lambda (str) (mapcar #'cl-caddr - (cider-sync-request:complete str ":same"))))) + (with-no-warnings + (cider-sync-request:complete str ":same")))))) -(defun couns-git () +(defun counsel-git () "Find file in the current Git repository." (interactive) (let* ((default-directory (locate-dominating-file diff --git a/packages/swiper/ivy.el b/packages/swiper/ivy.el index 180f081..38a8ff4 100644 --- a/packages/swiper/ivy.el +++ b/packages/swiper/ivy.el @@ -4,7 +4,7 @@ ;; Author: Oleh Krehel <ohwoeo...@gmail.com> ;; URL: https://github.com/abo-abo/swiper -;; Version: 0.2.0 +;; Version: 0.2.1 ;; Package-Requires: ((emacs "24.1")) ;; Keywords: matching @@ -59,6 +59,11 @@ Set this to nil if you don't want the count." "Whether to wrap around after the first and last candidate." :type 'boolean) +(defcustom ivy-on-del-error-function 'minibuffer-keyboard-quit + "The handler for when `ivy-backward-delete-char' throws. +This is usually meant as a quick exit out of the minibuffer." + :type 'function) + ;;* User Visible ;;** Keymap (require 'delsel) @@ -105,43 +110,41 @@ of `history-length', which see.") (interactive) (setq ivy--index (1- ivy--length))) -(defun ivy-next-line () - "Select the next completion candidate." - (interactive) - (if (>= ivy--index (1- ivy--length)) - (when ivy-wrap - (ivy-beginning-of-buffer)) - (cl-incf ivy--index))) - -(defun ivy-next-line-or-history () - "Select the next completion candidate. +(defun ivy-next-line (&optional arg) + "Move cursor vertically down ARG candidates." + (interactive "p") + (setq arg (or arg 1)) + (cl-incf ivy--index arg) + (when (>= ivy--index (1- ivy--length)) + (if ivy-wrap + (ivy-beginning-of-buffer) + (setq ivy--index (1- ivy--length))))) + +(defun ivy-next-line-or-history (&optional arg) + "Move cursor vertically down ARG candidates. If the input is empty, select the previous history element instead." - (interactive) + (interactive "p") (when (string= ivy-text "") (ivy-previous-history-element 1)) - (if (>= ivy--index (1- ivy--length)) - (when ivy-wrap - (ivy-beginning-of-buffer)) - (cl-incf ivy--index))) - -(defun ivy-previous-line () - "Select the previous completion candidate." - (interactive) - (if (zerop ivy--index) - (when ivy-wrap - (ivy-end-of-buffer)) - (cl-decf ivy--index))) + (ivy-next-line arg)) -(defun ivy-previous-line-or-history () - "Select the previous completion candidate. +(defun ivy-previous-line (&optional arg) + "Move cursor vertically up ARG candidates." + (interactive "p") + (setq arg (or arg 1)) + (cl-decf ivy--index arg) + (when (< ivy--index 0) + (if ivy-wrap + (ivy-end-of-buffer) + (setq ivy--index 0)))) + +(defun ivy-previous-line-or-history (arg) + "Move cursor vertically up ARG candidates. If the input is empty, select the previous history element instead." - (interactive) + (interactive "p") (when (string= ivy-text "") (ivy-previous-history-element 1)) - (if (zerop ivy--index) - (when ivy-wrap - (ivy-end-of-buffer)) - (cl-decf ivy--index))) + (ivy-previous-line arg)) (defun ivy-previous-history-element (arg) "Forward to `previous-history-element' with ARG." @@ -157,12 +160,13 @@ If the input is empty, select the previous history element instead." (defun ivy-backward-delete-char () "Forward to `backward-delete-char'. -On error (read-only), quit without selecting." +On error (read-only), call `ivy-on-del-error-function'." (interactive) (condition-case nil (backward-delete-char 1) (error - (minibuffer-keyboard-quit)))) + (when ivy-on-del-error-function + (funcall ivy-on-del-error-function))))) ;;** Entry Point (defun ivy-read (prompt collection @@ -172,17 +176,18 @@ On error (read-only), quit without selecting." PROMPT is a string to prompt with; normally it ends in a colon and a space. When PROMPT contains %d, it will be updated with the current number of matching candidates. +See also `ivy-count-format'. COLLECTION is a list of strings. If INITIAL-INPUT is non-nil, insert it in the minibuffer initially. -UPDATE-FN is called each time the current candidate(s) is changed. +KEYMAP is composed together with `ivy-minibuffer-map'. If PRESELECT is non-nil select the corresponding candidate out of the ones that match INITIAL-INPUT. -KEYMAP is composed together with `ivy-minibuffer-map'." +UPDATE-FN is called each time the current candidate(s) is changed." (cl-case (length collection) (0 nil) (1 (car collection)) @@ -226,6 +231,45 @@ KEYMAP is composed together with `ivy-minibuffer-map'." (when ivy--action (funcall ivy--action)))))) +(defun ivy-completing-read (prompt collection + &optional predicate _require-match initial-input + &rest _ignore) + "Read a string in the minibuffer, with completion. + +This is an interface that conforms to `completing-read', so that +it can be used for `completing-read-function'. + +PROMPT is a string to prompt with; normally it ends in a colon and a space. +COLLECTION can be a list of strings, an alist, an obarray or a hash table. +PREDICATE limits completion to a subset of COLLECTION. + +_REQUIRE-MATCH is ignored for now. +INITIAL-INPUT is a string that can be inserted into the minibuffer initially. + +The history, defaults and input-method arguments are ignored for now." + (cond ((functionp collection) + (error "Function as a collection unsupported")) + ((hash-table-p collection) + (error "Hash table as a collection unsupported")) + ((listp (car collection)) + (setq collection (mapcar #'car collection)))) + (when predicate + (setq collection (cl-remove-if-not predicate collection))) + (ivy-read prompt collection initial-input)) + +;;;###autoload +(define-minor-mode ivy-mode + "Toggle Ivy mode on or off. +With ARG, turn Ivy mode on if arg is positive, off otherwise. +Turning on Ivy mode will set `completing-read-function' to +`ivy-completing-read'." + :group 'ivy + :global t + :lighter " ivy" + (if ivy-mode + (setq completing-read-function 'ivy-completing-read) + (setq completing-read-function 'completing-read-default))) + (defvar ivy--action nil "Store a function to call at the end of `ivy--read'.") @@ -384,7 +428,6 @@ CANDIDATES is a list of strings." (cands (if (and (equal re ivy--old-re) ivy--old-cands) ivy--old-cands - (setq ivy--old-re re) (ignore-errors (cl-remove-if-not (lambda (x) (string-match re x)) @@ -392,12 +435,15 @@ CANDIDATES is a list of strings." (tail (nthcdr ivy--index ivy--old-cands)) (ww (window-width)) idx) - (setq ivy--length (length cands)) (when (and tail ivy--old-cands) - (while (and tail - (null (setq idx (cl-position (pop tail) cands - :test #'equal))))) - (setq ivy--index (or idx 0))) + (unless (and (not (equal re ivy--old-re)) + (setq ivy--index (cl-position re cands :test 'equal))) + (while (and tail (null idx)) + ;; Compare with eq to handle equal duplicates in cands + (setq idx (cl-position (pop tail) cands))) + (setq ivy--index (or idx 0)))) + (setq ivy--old-re re) + (setq ivy--length (length cands)) (setq ivy--old-cands cands) (when (>= ivy--index ivy--length) (setq ivy--index (max (1- ivy--length) 0))) diff --git a/packages/swiper/swiper.el b/packages/swiper/swiper.el index b3b1940..a3143f7 100644 --- a/packages/swiper/swiper.el +++ b/packages/swiper/swiper.el @@ -4,8 +4,8 @@ ;; Author: Oleh Krehel <ohwoeo...@gmail.com> ;; URL: https://github.com/abo-abo/swiper -;; Version: 0.2.0 -;; Package-Requires: ((emacs "24.1")) +;; Version: 0.2.1 +;; Package-Requires: ((emacs "24.1") (ivy "0.2.1")) ;; Keywords: matching ;; This file is part of GNU Emacs. @@ -75,6 +75,7 @@ (defvar swiper-map (let ((map (make-sparse-keymap))) (define-key map (kbd "M-q") 'swiper-query-replace) + (define-key map (kbd "C-l") 'swiper-recenter-top-bottom) map) "Keymap for swiper.") @@ -83,7 +84,8 @@ (interactive) (if (null (window-minibuffer-p)) (user-error "Should only be called in the minibuffer through `swiper-map'") - (let* ((from (ivy--regex ivy-text)) + (let* ((enable-recursive-minibuffers t) + (from (ivy--regex ivy-text)) (to (query-replace-read-to from "Query replace" t))) (delete-minibuffer-contents) (setq ivy--action @@ -93,6 +95,12 @@ (swiper--cleanup) (exit-minibuffer)))) +(defun swiper-recenter-top-bottom (&optional arg) + "Call (`recenter-top-bottom' ARG) in `swiper--window'." + (interactive "P") + (with-selected-window swiper--window + (recenter-top-bottom arg))) + (defvar swiper--window nil "Store the current window.") @@ -103,7 +111,8 @@ gnus-summary-mode gnus-article-mode gnus-group-mode - emms-playlist-mode erc-mode))) + emms-playlist-mode erc-mode + org-agenda-mode))) (if (fboundp 'font-lock-ensure) (font-lock-ensure) (font-lock-fontify-buffer)))) @@ -129,7 +138,7 @@ (line-beginning-position) (line-end-position))) candidates) - (zerop (forward-line 1))) + (forward-line 1)) (nreverse candidates)))))) (defvar swiper--opoint 1 @@ -221,8 +230,8 @@ When non-nil, INITIAL-INPUT is the initial search pattern." (forward-line (1- num)) (isearch-range-invisible (line-beginning-position) (line-end-position)) - (unless (and (> (point) (window-start)) - (< (point) (window-end swiper--window t))) + (unless (and (>= (point) (window-start)) + (<= (point) (window-end swiper--window t))) (recenter))) (let ((ov (make-overlay (line-beginning-position)