emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master 302a16a 67/67: Add 'packages/swiper/' from commit '55414c3


From: Oleh Krehel
Subject: [elpa] master 302a16a 67/67: Add 'packages/swiper/' from commit '55414c321ca07bd86f0f1efaf8f6130617e6fad6'
Date: Sun, 22 Mar 2015 17:34:14 +0000

branch: master
commit 302a16a15bbaf3842246293a27c77ba2fd9a56e1
Merge: 5aa7896 55414c3
Author: Oleh Krehel <address@hidden>
Commit: Oleh Krehel <address@hidden>

    Add 'packages/swiper/' from commit 
'55414c321ca07bd86f0f1efaf8f6130617e6fad6'
    
    git-subtree-dir: packages/swiper
    git-subtree-mainline: 5aa78963734dc6975cad8df1c8853b65a4f1b826
    git-subtree-split: 55414c321ca07bd86f0f1efaf8f6130617e6fad6
---
 packages/swiper/.travis.yml |   12 ++
 packages/swiper/Makefile    |   16 ++
 packages/swiper/README.md   |   16 ++
 packages/swiper/counsel.el  |   77 ++++++++
 packages/swiper/ivy-test.el |   67 +++++++
 packages/swiper/ivy.el      |  413 +++++++++++++++++++++++++++++++++++++++++++
 packages/swiper/swiper.el   |  255 ++++++++++++++++++++++++++
 7 files changed, 856 insertions(+), 0 deletions(-)

diff --git a/packages/swiper/.travis.yml b/packages/swiper/.travis.yml
new file mode 100644
index 0000000..1f5dbc7
--- /dev/null
+++ b/packages/swiper/.travis.yml
@@ -0,0 +1,12 @@
+language: emacs-lisp
+env:
+  matrix:
+    - EMACS=emacs24
+
+before_install:
+  - sudo add-apt-repository -y ppa:cassou/emacs
+  - sudo apt-get update -qq
+  - sudo apt-get install -qq $EMACS
+
+script:
+  - make test
diff --git a/packages/swiper/Makefile b/packages/swiper/Makefile
new file mode 100644
index 0000000..453f709
--- /dev/null
+++ b/packages/swiper/Makefile
@@ -0,0 +1,16 @@
+emacs ?= emacs
+
+LOAD = -l ivy.el -l swiper.el
+
+.PHONY: all compile clean
+
+all: test
+
+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\"))"
+
+clean:
+       rm -f *.elc
diff --git a/packages/swiper/README.md b/packages/swiper/README.md
new file mode 100644
index 0000000..6482463
--- /dev/null
+++ b/packages/swiper/README.md
@@ -0,0 +1,16 @@
+[![Build 
Status](https://travis-ci.org/abo-abo/swiper.svg?branch=master)](https://travis-ci.org/abo-abo/swiper)
+
+## Swiper
+
+Package for GNU Emacs that gives you an overview as you search for a regex
+
+![swiper.png](http://oremacs.com/download/swiper.png)
+
+The package uses the `ivy` back end for the overview, see also
+[swiper-helm](https://github.com/abo-abo/swiper-helm).
+
+## Screenshots
+
+![ivy-swiper-1.png](http://oremacs.com/download/ivy-swiper-1.png)
+
+There's also a one minute [video 
demo](https://www.youtube.com/watch?v=s3qwiAtKjuA).
diff --git a/packages/swiper/counsel.el b/packages/swiper/counsel.el
new file mode 100644
index 0000000..07d9a66
--- /dev/null
+++ b/packages/swiper/counsel.el
@@ -0,0 +1,77 @@
+;;; consel.el --- Elisp completion at point -*- lexical-binding: t -*-
+
+;; Copyright (C) 2015  Free Software Foundation, Inc.
+
+;; Author: Oleh Krehel <address@hidden>
+
+;; This file is part of GNU Emacs.
+
+;; This file is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; For a full copy of the GNU General Public License
+;; see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; Just call `counsel' to start completing the `obarray'.
+;; The initial (optional) input is thing-at-point.
+
+;;; Code:
+
+(require 'ivy)
+
+(defun counsel ()
+  "Elisp completion at point."
+  (interactive)
+  (counsel--generic
+   (lambda (str) (all-completions str obarray))))
+
+(defun couns-clj ()
+  "Clojure completion at point."
+  (interactive)
+  (counsel--generic
+   (lambda (str)
+     (mapcar
+      #'cl-caddr
+      (cider-sync-request:complete str ":same")))))
+
+(defun couns-git ()
+  "Find file in the current Git repository."
+  (interactive)
+  (let* ((default-directory (locate-dominating-file
+                             default-directory ".git"))
+         (cands (split-string
+                 (shell-command-to-string
+                  "git ls-files --full-name --")
+                 "\n"))
+         (file (ivy-read "Find file: " cands)))
+    (when file
+      (find-file file))))
+
+(defun counsel--generic (completion-fn)
+  "Complete thing at point with COMPLETION-FN."
+  (let* ((bnd (bounds-of-thing-at-point 'symbol))
+         (str (if bnd
+                  (buffer-substring-no-properties
+                   (car bnd) (cdr bnd))
+                ""))
+         (candidates (funcall completion-fn str))
+         (ivy-height 7)
+         (res (ivy-read (format "pattern (%s): " str)
+                        candidates)))
+    (when (stringp res)
+      (when bnd
+        (delete-region (car bnd) (cdr bnd)))
+      (insert res))))
+
+(provide 'counsel)
+
+;;; counsel.el ends here
diff --git a/packages/swiper/ivy-test.el b/packages/swiper/ivy-test.el
new file mode 100644
index 0000000..a33f886
--- /dev/null
+++ b/packages/swiper/ivy-test.el
@@ -0,0 +1,67 @@
+;;; ivy-test.el --- tests for ivy
+
+;; Copyright (C) 2015  Free Software Foundation, Inc.
+
+;; Author: Oleh Krehel
+
+;; This file is part of GNU Emacs.
+
+;; This file is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; For a full copy of the GNU General Public License
+;; see <http://www.gnu.org/licenses/>.
+
+(require 'ert)
+
+(defvar ivy-expr nil
+  "Holds a test expression to evaluate with `ivy-eval'.")
+
+(defvar ivy-result nil
+  "Holds the eval result of `ivy-expr' by `ivy-eval'.")
+
+(defun ivy-eval ()
+  "Evaluate `ivy-expr'."
+  (interactive)
+  (setq ivy-result (eval ivy-expr)))
+
+(global-set-key (kbd "C-c e") 'ivy-eval)
+
+(defun ivy-with (expr keys)
+  "Evaluate EXPR followed by KEYS."
+  (let ((ivy-expr expr))
+    (execute-kbd-macro
+     (vconcat (kbd "C-c e")
+              (kbd keys)))
+    ivy-result))
+
+(ert-deftest ivy-read ()
+  (should (equal
+           (ivy-read "pattern: " nil)
+           nil))
+  (should (equal
+           (ivy-read "pattern: " '("42"))
+           "42"))
+  (should (equal
+           (ivy-with '(ivy-read "pattern: " '("blue" "yellow"))
+                     "C-m")
+           "blue"))
+  (should (equal
+           (ivy-with '(ivy-read "pattern: " '("blue" "yellow"))
+                     "y C-m")
+           "yellow"))
+  (should (equal
+           (ivy-with '(ivy-read "pattern: " '("blue" "yellow"))
+                     "y DEL b C-m")
+           "blue"))
+  (should (equal
+           (ivy-with '(ivy-read "pattern: " '("blue" "yellow"))
+                     "z C-m")
+           nil)))
diff --git a/packages/swiper/ivy.el b/packages/swiper/ivy.el
new file mode 100644
index 0000000..4d307f3
--- /dev/null
+++ b/packages/swiper/ivy.el
@@ -0,0 +1,413 @@
+;;; ivy.el --- Incremental Vertical completYon -*- lexical-binding: t -*-
+
+;; Copyright (C) 2015  Free Software Foundation, Inc.
+
+;; Author: Oleh Krehel <address@hidden>
+;; URL: https://github.com/abo-abo/swiper
+;; Version: 0.1.0
+;; Package-Requires: ((emacs "24.1"))
+;; Keywords: matching
+
+;; This file is part of GNU Emacs.
+
+;; This file is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; For a full copy of the GNU General Public License
+;; see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; This package provides `ivy-read' as an alternative to
+;; `completing-read' and similar functions.
+;;
+;; There's no intricate code to determine the best candidate.
+;; Instead, the user can navigate to it with `ivy-next-line' and
+;; `ivy-previous-line'.
+;;
+;; The matching is done by splitting the input text by spaces and
+;; re-building it into a regex.
+;; So "for example" is transformed into "\\(for\\).*\\(example\\)".
+
+;;; Code:
+;;* Customization
+(defgroup ivy nil
+  "Incremental vertical completion."
+  :group 'convenience)
+
+(defface ivy-current-match
+  '((t (:inherit highlight)))
+  "Face used by Ivy for highlighting first match.")
+
+(defcustom ivy-height 10
+  "Number of lines for the minibuffer window."
+  :type 'integer)
+
+(defcustom ivy-count-format "%-4d "
+  "The style of showing the current candidate count for `ivy-read'.
+Set this to nil if you don't want the count."
+  :type 'string)
+
+(defcustom ivy-wrap nil
+  "Whether to wrap around after the first and last candidate."
+  :type 'boolean)
+
+;;* User Visible
+;;** Keymap
+(require 'delsel)
+(defvar ivy-minibuffer-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map (kbd "C-m") 'ivy-done)
+    (define-key map (kbd "C-n") 'ivy-next-line)
+    (define-key map (kbd "C-p") 'ivy-previous-line)
+    (define-key map (kbd "C-s") 'ivy-next-line-or-history)
+    (define-key map (kbd "C-r") 'ivy-previous-line-or-history)
+    (define-key map (kbd "SPC") 'self-insert-command)
+    (define-key map (kbd "DEL") 'ivy-backward-delete-char)
+    (define-key map (kbd "M-<") 'ivy-beginning-of-buffer)
+    (define-key map (kbd "M->") 'ivy-end-of-buffer)
+    (define-key map (kbd "M-n") 'ivy-next-history-element)
+    (define-key map (kbd "M-p") 'ivy-previous-history-element)
+    (define-key map (kbd "C-g") 'minibuffer-keyboard-quit)
+    map)
+  "Keymap used in the minibuffer.")
+
+(defvar ivy-history nil
+  "History list of candidates entered in the minibuffer.
+
+Maximum length of the history list is determined by the value
+of `history-length', which see.")
+
+;;** Commands
+(defun ivy-done ()
+  "Exit the minibuffer with the selected candidate."
+  (interactive)
+  (delete-minibuffer-contents)
+  (unless (zerop ivy--length)
+    (insert ivy--current)
+    (setq ivy-exit 'done))
+  (exit-minibuffer))
+
+(defun ivy-beginning-of-buffer ()
+  "Select the first completion candidate."
+  (interactive)
+  (setq ivy--index 0))
+
+(defun ivy-end-of-buffer ()
+  "Select the last completion candidate."
+  (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.
+If the input is empty, select the previous history element instead."
+  (interactive)
+  (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)))
+
+(defun ivy-previous-line-or-history ()
+  "Select the previous completion candidate.
+If the input is empty, select the previous history element instead."
+  (interactive)
+  (when (string= ivy-text "")
+    (ivy-previous-history-element 1))
+  (if (zerop ivy--index)
+      (when ivy-wrap
+        (ivy-end-of-buffer))
+    (cl-decf ivy--index)))
+
+(defun ivy-previous-history-element (arg)
+  "Forward to `previous-history-element' with ARG."
+  (interactive "p")
+  (previous-history-element arg)
+  (move-end-of-line 1))
+
+(defun ivy-next-history-element (arg)
+  "Forward to `next-history-element' with ARG."
+  (interactive "p")
+  (next-history-element arg)
+  (move-end-of-line 1))
+
+(defun ivy-backward-delete-char ()
+  "Forward to `backward-delete-char'.
+On error (read-only), quit without selecting."
+  (interactive)
+  (condition-case nil
+      (backward-delete-char 1)
+    (error
+     (minibuffer-keyboard-quit))))
+
+;;** Entry Point
+(defun ivy-read (prompt collection &optional initial-input update-fn preselect)
+  "Read a string in the minibuffer, with completion.
+
+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.
+
+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.
+
+If PRESELECT is non-nil select the corresponding candidate out of
+the ones that match INITIAL-INPUT."
+  (cl-case (length collection)
+    (0 nil)
+    (1 (car collection))
+    (t
+     (setq ivy--index (or
+                       (and preselect
+                            (ivy--preselect-index
+                             collection initial-input preselect))
+                       0))
+     (setq ivy--old-re nil)
+     (setq ivy--old-cands nil)
+     (setq ivy-text "")
+     (setq ivy--all-candidates collection)
+     (setq ivy--update-fn update-fn)
+     (setq ivy-exit nil)
+     (setq ivy--default (or (thing-at-point 'symbol) ""))
+     (setq ivy--prompt
+           (cond ((string-match "%.*d" prompt)
+                  prompt)
+                 ((string-match "%.*d" ivy-count-format)
+                  (concat ivy-count-format prompt))
+                 (t
+                  nil)))
+     (unwind-protect
+          (minibuffer-with-setup-hook
+              #'ivy--minibuffer-setup
+            (let ((res (read-from-minibuffer
+                        prompt
+                        initial-input
+                        ivy-minibuffer-map
+                        nil
+                        'ivy-history)))
+              (when (eq ivy-exit 'done)
+                (pop ivy-history)
+                (setq ivy-history
+                      (cons ivy-text (delete ivy-text ivy-history)))
+                res)))
+       (remove-hook 'post-command-hook #'ivy--exhibit)))))
+
+(defun ivy--preselect-index (candidates initial-input preselect)
+  "Return the index in CANDIDATES filtered by INITIAL-INPUT for PRESELECT."
+  (when initial-input
+    (setq candidates
+          (cl-remove-if-not
+           (lambda (x)
+             (string-match initial-input x))
+           candidates)))
+  (cl-position-if
+   (lambda (x)
+     (string-match preselect x))
+   candidates))
+
+(defvar ivy-text ""
+  "Stores the user's string as it is typed in.")
+
+(defvar ivy-exit nil
+  "Store 'done if the completion was successfully selected.
+Otherwise, store nil.")
+
+;;* Implementation
+;;** Regex
+(defvar ivy--subexps 0
+  "Number of groups in the current `ivy--regex'.")
+
+(defvar ivy--regex-hash
+  (make-hash-table :test 'equal)
+  "Store pre-computed regex.")
+
+(defun ivy--regex (str)
+  "Re-build regex from STR in case it has a space."
+  (let ((hashed (gethash str ivy--regex-hash)))
+    (if hashed
+        (prog1 (cdr hashed)
+          (setq ivy--subexps (car hashed)))
+      (cdr (puthash str
+                    (let ((subs (split-string str " +" t)))
+                      (if (= (length subs) 1)
+                          (cons
+                           (setq ivy--subexps 0)
+                           (car subs))
+                        (cons
+                         (setq ivy--subexps (length subs))
+                         (mapconcat
+                          (lambda (x) (format "\\(%s\\)" x))
+                          subs
+                          ".*"))))
+                    ivy--regex-hash)))))
+
+;;** Rest
+(defun ivy--minibuffer-setup ()
+  "Setup ivy completion in the minibuffer."
+  (set (make-local-variable 'completion-show-inline-help) nil)
+  (set (make-local-variable 'minibuffer-default-add-function)
+       (lambda ()
+         (list ivy--default)))
+  (use-local-map (make-composed-keymap ivy-minibuffer-map
+                                       (current-local-map)))
+  (setq-local max-mini-window-height ivy-height)
+  (add-hook 'post-command-hook #'ivy--exhibit nil t)
+  ;; show completions with empty input
+  (ivy--exhibit))
+
+(defvar ivy--all-candidates nil
+  "Store the candidates passed to `ivy-read'.")
+
+(defvar ivy--index 0
+  "Store the index of the current candidate.")
+
+(defvar ivy--length 0
+  "Store the amount of viable candidates.")
+
+(defvar ivy--current ""
+  "Current candidate.")
+
+(defvar ivy--default nil
+  "Default initial input.")
+
+(defvar ivy--update-fn nil
+  "Current function to call when current candidate(s) update.")
+
+(defun ivy--input ()
+  "Return the current minibuffer input."
+  ;; assume one-line minibuffer input
+  (buffer-substring-no-properties
+   (minibuffer-prompt-end)
+   (line-end-position)))
+
+(defun ivy--cleanup ()
+  "Delete the displayed completion candidates."
+  (save-excursion
+    (goto-char (minibuffer-prompt-end))
+    (delete-region (line-end-position) (point-max))))
+
+(defvar ivy--prompt nil
+  "Store the format-style prompt.
+When non-nil, it should contain one %d.")
+
+(defun ivy--insert-prompt ()
+  "Update the prompt according to `ivy--prompt'."
+  (when ivy--prompt
+    (let ((inhibit-read-only t)
+          (n-str (format ivy--prompt ivy--length)))
+      (save-excursion
+        (goto-char (point-min))
+        (delete-region (point-min) (minibuffer-prompt-end))
+        (set-text-properties
+         0 (length n-str)
+         '(front-sticky t rear-nonsticky t field t read-only t face 
minibuffer-prompt)
+         n-str)
+        (insert n-str))
+      ;; get out of the prompt area
+      (constrain-to-field nil (point-max)))))
+
+(defun ivy--exhibit ()
+  "Insert Ivy completions display.
+Should be run via minibuffer `post-command-hook'."
+  (setq ivy-text (ivy--input))
+  (ivy--cleanup)
+  (let ((text (ivy-completions
+               ivy-text
+               ivy--all-candidates))
+        (buffer-undo-list t)
+        deactivate-mark)
+    (when ivy--update-fn
+      (funcall ivy--update-fn))
+    (ivy--insert-prompt)
+    ;; Do nothing if while-no-input was aborted.
+    (when (stringp text)
+      (save-excursion
+        (forward-line 1)
+        (insert text)))))
+
+(defvar ivy--old-re nil
+  "Store the old regexp.")
+
+(defvar ivy--old-cands nil
+  "Store the candidates matched by `ivy--old-re'.")
+
+(defun ivy--add-face (str face)
+  "Propertize STR with FACE.
+`font-lock-append-text-property' is used, since it's better than
+`propertize' or `add-face-text-property' in this case."
+  (font-lock-append-text-property 0 (length str) 'face face str)
+  str)
+
+(defun ivy-completions (name candidates)
+  "Return as text the current completions.
+NAME is a string of words separated by spaces that is used to
+build a regex.
+CANDIDATES is a list of strings."
+  (let* ((re (ivy--regex name))
+         (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))
+                     candidates))))
+         (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)))
+    (setq ivy--old-cands cands)
+    (when (>= ivy--index ivy--length)
+      (setq ivy--index (max (1- ivy--length) 0)))
+    (if (null cands)
+        ""
+      (let* ((half-height (/ ivy-height 2))
+             (start (max 0 (- ivy--index half-height)))
+             (end (min (+ start (1- ivy-height)) ivy--length))
+             (cands (cl-subseq cands start end))
+             (index (min ivy--index half-height (1- (length cands)))))
+        (setq ivy--current (copy-sequence (nth index cands)))
+        (setf (nth index cands)
+              (ivy--add-face ivy--current 'ivy-current-match))
+        (concat "\n" (mapconcat
+                      (lambda (s)
+                        (if (> (length s) ww)
+                            (concat (substring s 0 (- ww 3)) "...")
+                          s))
+                      cands "\n"))))))
+
+(provide 'ivy)
+
+;;; ivy.el ends here
diff --git a/packages/swiper/swiper.el b/packages/swiper/swiper.el
new file mode 100644
index 0000000..1be8676
--- /dev/null
+++ b/packages/swiper/swiper.el
@@ -0,0 +1,255 @@
+;;; swiper.el --- Isearch with an overview. Oh, man! -*- lexical-binding: t -*-
+
+;; Copyright (C) 2015  Free Software Foundation, Inc.
+
+;; Author: Oleh Krehel <address@hidden>
+;; URL: https://github.com/abo-abo/swiper
+;; Version: 0.1.0
+;; Package-Requires: ((emacs "24.1") (ivy "0.1.0"))
+;; Keywords: matching
+
+;; This file is part of GNU Emacs.
+
+;; This file is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; For a full copy of the GNU General Public License
+;; see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; This package gives an overview of the current regex search
+;; candidates.  The search regex can be split into groups with a
+;; space.  Each group is highlighted with a different face.
+;;
+;; The overview back end is `ivy'.
+;;
+;; It can double as a quick `regex-builder', although only single
+;; lines will be matched.
+
+;;; Code:
+(require 'ivy)
+
+(defgroup swiper nil
+  "`isearch' with an overview."
+  :group 'matching
+  :prefix "swiper-")
+
+(defface swiper-match-face-1
+  '((t (:inherit isearch-lazy-highlight-face)))
+  "Face for `swiper' matches.")
+
+(defface swiper-match-face-2
+  '((t (:inherit isearch)))
+  "Face for `swiper' matches.")
+
+(defface swiper-match-face-3
+  '((t (:inherit match)))
+  "Face for `swiper' matches.")
+
+(defface swiper-match-face-4
+  '((t (:inherit isearch)))
+  "Face for `swiper' matches.")
+
+(defface swiper-line-face
+  '((t (:inherit highlight)))
+  "Face for current `swiper' line.")
+
+(defcustom swiper-faces '(swiper-match-face-1
+                          swiper-match-face-2
+                          swiper-match-face-3
+                          swiper-match-face-4)
+  "List of `swiper' faces for group matches.")
+
+(defcustom swiper-min-highlight 2
+  "Only highlight matches for regexps at least this long."
+  :type 'integer)
+
+(defvar swiper--window nil
+  "Store the current window.")
+
+(defun swiper-font-lock-ensure ()
+  "Ensure the entired buffer is highlighted."
+  (unless (or (derived-mode-p 'magit-mode)
+              (memq major-mode '(package-menu-mode emms-playlist-mode)))
+    (if (fboundp 'font-lock-ensure)
+        (font-lock-ensure)
+      (font-lock-fontify-buffer))))
+
+(defvar swiper--format-spec ""
+  "Store the current candidates format spec.")
+
+(defun swiper--candidates ()
+  "Return a list of this buffer lines."
+  (let ((n-lines (count-lines (point-min) (point-max))))
+    (unless (zerop n-lines)
+      (setq swiper--format-spec
+            (format "%%-%dd %%s" (1+ (floor (log n-lines 10)))))
+      (let ((line-number 0)
+            candidates)
+        (save-excursion
+          (goto-char (point-min))
+          (swiper-font-lock-ensure)
+          (while (< (point) (point-max))
+            (push (format swiper--format-spec
+                          (cl-incf line-number)
+                          (buffer-substring
+                           (line-beginning-position)
+                           (line-end-position)))
+                  candidates)
+            (zerop (forward-line 1)))
+          (nreverse candidates))))))
+
+(defvar swiper--opoint 1
+  "The point when `swiper' starts.")
+
+;;;###autoload
+(defun swiper (&optional initial-input)
+  "`isearch' with an overview.
+When non-nil, INITIAL-INPUT is the initial search pattern."
+  (interactive)
+  (swiper--ivy initial-input))
+
+(defun swiper--init ()
+  "Perform initialization common to both completion methods."
+  (deactivate-mark)
+  (setq swiper--opoint (point))
+  (setq swiper--len 0)
+  (setq swiper--anchor (line-number-at-pos))
+  (setq swiper--window (selected-window)))
+
+(defun swiper--ivy (&optional initial-input)
+  "`isearch' with an overview using `ivy'.
+When non-nil, INITIAL-INPUT is the initial search pattern."
+  (interactive)
+  (ido-mode -1)
+  (swiper--init)
+  (let ((candidates (swiper--candidates))
+        (preselect (format
+                    swiper--format-spec
+                    (line-number-at-pos)
+                    (regexp-quote
+                     (buffer-substring-no-properties
+                      (line-beginning-position)
+                      (line-end-position)))))
+        res)
+    (unwind-protect
+         (setq res (ivy-read
+                    (replace-regexp-in-string
+                     "%s" "pattern: " swiper--format-spec)
+                    candidates
+                    initial-input
+                    #'swiper--update-input-ivy
+                    preselect))
+      (ido-mode 1)
+      (swiper--cleanup)
+      (if (null ivy-exit)
+          (goto-char swiper--opoint)
+        (swiper--action res ivy-text)))))
+
+(defun swiper--ensure-visible ()
+  "Remove overlays hiding point."
+  (let ((overlays (overlays-at (point)))
+        ov expose)
+    (while (setq ov (pop overlays))
+      (if (and (invisible-p (overlay-get ov 'invisible))
+               (setq expose (overlay-get ov 'isearch-open-invisible)))
+          (funcall expose ov)))))
+
+(defun swiper--cleanup ()
+  "Clean up the overlays."
+  (while swiper--overlays
+    (delete-overlay (pop swiper--overlays)))
+  (save-excursion
+    (goto-char (point-min))
+    (isearch-clean-overlays)))
+
+(defvar swiper--overlays nil
+  "Store overlays.")
+
+(defvar swiper--anchor nil
+  "A line number to which the search should be anchored.")
+
+(defvar swiper--len 0
+  "The last length of input for which an anchoring was made.")
+
+(defun swiper--update-input-ivy ()
+  "Called when `ivy' input is updated."
+  (swiper--cleanup)
+  (let* ((re (ivy--regex ivy-text))
+         (str ivy--current)
+         (num (if (string-match "^[0-9]+" str)
+                  (string-to-number (match-string 0 str))
+                0)))
+    (with-selected-window swiper--window
+      (goto-char (point-min))
+      (when (cl-plusp num)
+        (goto-char (point-min))
+        (forward-line (1- num))
+        (isearch-range-invisible (line-beginning-position)
+                                 (line-end-position))
+        (unless (and (> (point) (window-start))
+                     (< (point) (window-end swiper--window t)))
+          (recenter)))
+      (let ((ov (make-overlay
+                 (line-beginning-position)
+                 (1+ (line-end-position)))))
+        (overlay-put ov 'face 'swiper-line-face)
+        (overlay-put ov 'window swiper--window)
+        (push ov swiper--overlays))
+      (swiper--add-overlays
+       re
+       (window-start swiper--window)
+       (window-end swiper--window t)))))
+
+(defun swiper--add-overlays (re beg end)
+  "Add overlays for RE regexp in current buffer between BEG and END."
+  (when (>= (length re) swiper-min-highlight)
+    (save-excursion
+      (goto-char beg)
+      ;; RE can become an invalid regexp
+      (while (and (ignore-errors (re-search-forward re end t))
+                  (> (- (match-end 0) (match-beginning 0)) 0))
+        (let ((i 0))
+          (while (<= i ivy--subexps)
+            (when (match-beginning i)
+              (let ((overlay (make-overlay (match-beginning i)
+                                           (match-end i)))
+                    (face
+                     (cond ((zerop ivy--subexps)
+                            (cl-caddr swiper-faces))
+                           ((zerop i)
+                            (car swiper-faces))
+                           (t
+                            (nth (1+ (mod (1- i) (1- (length swiper-faces))))
+                                 swiper-faces)))))
+                (push overlay swiper--overlays)
+                (overlay-put overlay 'face face)
+                (overlay-put overlay 'window swiper--window)
+                (overlay-put overlay 'priority i)))
+            (cl-incf i)))))))
+
+(defun swiper--action (x input)
+  "Goto line X and search for INPUT."
+  (if (null x)
+      (user-error "No candidates")
+    (goto-char (point-min))
+    (forward-line (1- (read x)))
+    (re-search-forward
+     (ivy--regex input) (line-end-position) t)
+    (swiper--ensure-visible)
+    (when (/= (point) swiper--opoint)
+      (unless (and transient-mark-mode mark-active)
+        (push-mark swiper--opoint t)
+        (message "Mark saved where search started")))))
+
+(provide 'swiper)
+
+;;; swiper.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]