]> code.delx.au - gnu-emacs-elpa/blob - packages/avy/avy.el
Merge commit 'd76bcd7c0dcecb33e6955e25963028600c371588'
[gnu-emacs-elpa] / packages / avy / avy.el
1 ;;; avy.el --- set-based completion -*- lexical-binding: t -*-
2
3 ;; Copyright (C) 2015 Free Software Foundation, Inc.
4
5 ;; Author: Oleh Krehel <ohwoeowho@gmail.com>
6 ;; URL: https://github.com/abo-abo/avy
7 ;; Version: 0.2.1
8 ;; Package-Requires: ((emacs "24.1") (cl-lib "0.5"))
9 ;; Keywords: point, location
10
11 ;; This file is part of GNU Emacs.
12
13 ;; This file is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 3, or (at your option)
16 ;; any later version.
17
18 ;; This program is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
22
23 ;; For a full copy of the GNU General Public License
24 ;; see <http://www.gnu.org/licenses/>.
25
26 ;;; Commentary:
27 ;;
28 ;; This package provides a generic completion method based on building
29 ;; a balanced decision tree with each candidate being a leaf. To
30 ;; traverse the tree from the root to a desired leaf, typically a
31 ;; sequence of `read-char' can be used.
32 ;;
33 ;; In order for `read-char' to make sense, the tree needs to be
34 ;; visualized appropriately, with a character at each branch node. So
35 ;; this completion method works only for things that you can see on
36 ;; your screen, all at once:
37 ;;
38 ;; * character positions
39 ;; * word or subword start positions
40 ;; * line beginning positions
41 ;; * link positions
42 ;; * window positions
43 ;;
44 ;; If you're familiar with the popular `ace-jump-mode' package, this
45 ;; package does all that and more, without the implementation
46 ;; headache.
47
48 ;;; Code:
49 (require 'cl-lib)
50
51 ;;* Customization
52 (defgroup avy nil
53 "Jump to things tree-style."
54 :group 'convenience
55 :prefix "avy-")
56
57 (defcustom avy-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)
58 "Default keys for jumping."
59 :type '(repeat :tag "Keys" character))
60
61 (defcustom avy-keys-alist nil
62 "Alist of avy-jump commands to `avy-keys' overriding the default `avy-keys'."
63 :type '(alist
64 :key-type (choice :tag "Command"
65 (const avy-goto-char)
66 (const avy-goto-char-2)
67 (const avy-isearch)
68 (const avy-goto-line)
69 (const avy-goto-subword-0)
70 (const avy-goto-subword-1)
71 (const avy-goto-word-0)
72 (const avy-goto-word-1)
73 (const avy-copy-line)
74 (const avy-copy-region)
75 (const avy-move-line))
76 :value-type (repeat :tag "Keys" character)))
77
78 (defcustom avy-style 'pre
79 "The default method of displaying the overlays.
80 Use `avy-styles-alist' to customize this per-command."
81 :type '(choice
82 (const :tag "Pre" pre)
83 (const :tag "At" at)
84 (const :tag "At Full" at-full)
85 (const :tag "Post" post)))
86
87 (defcustom avy-styles-alist nil
88 "Alist of avy-jump commands to the style for each command.
89 If the commands isn't on the list, `avy-style' is used."
90 :type '(alist
91 :key-type (choice :tag "Command"
92 (const avy-goto-char)
93 (const avy-goto-char-2)
94 (const avy-isearch)
95 (const avy-goto-line)
96 (const avy-goto-subword-0)
97 (const avy-goto-subword-1)
98 (const avy-goto-word-0)
99 (const avy-goto-word-1)
100 (const avy-copy-line)
101 (const avy-copy-region)
102 (const avy-move-line))
103 :value-type (choice
104 (const :tag "Pre" pre)
105 (const :tag "At" at)
106 (const :tag "At Full" at-full)
107 (const :tag "Post" post))))
108
109 (defcustom avy-background nil
110 "When non-nil, a gray background will be added during the selection."
111 :type 'boolean)
112
113 (defcustom avy-all-windows t
114 "Determine the list of windows to consider in search of candidates."
115 :type
116 '(choice
117 (const :tag "All Frames" all-frames)
118 (const :tag "This Frame" t)
119 (const :tag "This Window" nil)))
120
121 (defcustom avy-case-fold-search t
122 "Non-nil if searches should ignore case."
123 :type 'boolean)
124
125 (defcustom avy-word-punc-regexp "[!-/:-@[-`{-~]"
126 "Regexp of punctuation chars that count as word starts for `avy-goto-word-1.
127 When nil, punctuation chars will not be matched.
128
129 \"[!-/:-@[-`{-~]\" will match all printable punctuation chars."
130 :type 'regexp)
131
132 (defface avy-lead-face-0
133 '((t (:foreground "white" :background "#4f57f9")))
134 "Face used for first non-terminating leading chars.")
135
136 (defface avy-lead-face-1
137 '((t (:foreground "white" :background "gray")))
138 "Face used for matched leading chars.")
139
140 (defface avy-lead-face
141 '((t (:foreground "white" :background "#e52b50")))
142 "Face used for the leading chars.")
143
144 (defface avy-background-face
145 '((t (:foreground "gray40")))
146 "Face for whole window background during selection.")
147
148 ;;* Internals
149 ;;** Tree
150 (defmacro avy-multipop (lst n)
151 "Remove LST's first N elements and return them."
152 `(if (<= (length ,lst) ,n)
153 (prog1 ,lst
154 (setq ,lst nil))
155 (prog1 ,lst
156 (setcdr
157 (nthcdr (1- ,n) (prog1 ,lst (setq ,lst (nthcdr ,n ,lst))))
158 nil))))
159
160 (defun avy-tree (lst keys)
161 "Coerce LST into a balanced tree.
162 The degree of the tree is the length of KEYS.
163 KEYS are placed appropriately on internal nodes."
164 (let ((len (length keys)))
165 (cl-labels
166 ((rd (ls)
167 (let ((ln (length ls)))
168 (if (< ln len)
169 (cl-pairlis keys
170 (mapcar (lambda (x) (cons 'leaf x)) ls))
171 (let ((ks (copy-sequence keys))
172 res)
173 (dolist (s (avy-subdiv ln len))
174 (push (cons (pop ks)
175 (if (eq s 1)
176 (cons 'leaf (pop ls))
177 (rd (avy-multipop ls s))))
178 res))
179 (nreverse res))))))
180 (rd lst))))
181
182 (defun avy-subdiv (n b)
183 "Distribute N in B terms in a balanced way."
184 (let* ((p (1- (floor (+ (log n b) 1e-6))))
185 (x1 (expt b p))
186 (x2 (* b x1))
187 (delta (- n x2))
188 (n2 (/ delta (- x2 x1)))
189 (n1 (- b n2 1)))
190 (append
191 (make-list n1 x1)
192 (list
193 (- n (* n1 x1) (* n2 x2)))
194 (make-list n2 x2))))
195
196 (defun avy-traverse (tree walker &optional recur-key)
197 "Traverse TREE generated by `avy-tree'.
198 WALKER is a function that takes KEYS and LEAF.
199
200 RECUR-KEY is used in recursion.
201
202 LEAF is a member of LST argument of `avy-tree'.
203
204 KEYS is the path from the root of `avy-tree' to LEAF."
205 (dolist (br tree)
206 (let ((key (cons (car br) recur-key)))
207 (if (eq (cadr br) 'leaf)
208 (funcall walker key (cddr br))
209 (avy-traverse (cdr br) walker key)))))
210
211 (defun avy-handler-default (char)
212 "The default hander for a bad CHAR."
213 (signal 'user-error (list "No such candidate" char))
214 (throw 'done nil))
215
216 (defvar avy-handler-function 'avy-handler-default
217 "A function to call for a bad `read-char' in `avy-read'.")
218
219 (defvar avy-current-path ""
220 "Store the current incomplete path during `avy-read'.")
221
222 (defun avy-read (tree display-fn cleanup-fn)
223 "Select a leaf from TREE using consecutive `read-char'.
224
225 DISPLAY-FN should take CHAR and LEAF and signify that LEAFs
226 associated with CHAR will be selected if CHAR is pressed. This is
227 commonly done by adding a CHAR overlay at LEAF position.
228
229 CLEANUP-FN should take no arguments and remove the effects of
230 multiple DISPLAY-FN invokations."
231 (catch 'done
232 (setq avy-current-path "")
233 (while tree
234 (let ((avy--leafs nil))
235 (avy-traverse tree
236 (lambda (path leaf)
237 (push (cons path leaf) avy--leafs)))
238 (dolist (x avy--leafs)
239 (funcall display-fn (car x) (cdr x))))
240 (let ((char (read-char))
241 branch)
242 (funcall cleanup-fn)
243 (if (setq branch (assoc char tree))
244 (if (eq (car (setq tree (cdr branch))) 'leaf)
245 (throw 'done (cdr tree))
246 (setq avy-current-path
247 (concat avy-current-path (string char))))
248 (funcall avy-handler-function char))))))
249
250 ;;** Rest
251 (defun avy-window-list ()
252 "Return a list of windows depending on `avy-all-windows'."
253 (cond ((eq avy-all-windows 'all-frames)
254 (cl-mapcan #'window-list (frame-list)))
255
256 ((eq avy-all-windows t)
257 (window-list))
258
259 ((null avy-all-windows)
260 (list (selected-window)))
261
262 (t
263 (error "Unrecognized option: %S" avy-all-windows))))
264
265 (defmacro avy-dowindows (flip &rest body)
266 "Depending on FLIP and `avy-all-windows' run BODY in each or selected window."
267 (declare (indent 1)
268 (debug (form body)))
269 `(let ((avy-all-windows (if ,flip
270 (not avy-all-windows)
271 avy-all-windows)))
272 (dolist (wnd (avy-window-list))
273 (with-selected-window wnd
274 (unless (memq major-mode '(image-mode doc-view-mode))
275 ,@body)))))
276
277 (defmacro avy--with-avy-keys (command &rest body)
278 "Set `avy-keys' according to COMMAND and execute BODY."
279 (declare (indent 1)
280 (debug (form body)))
281 `(let ((avy-keys (or (cdr (assq ',command avy-keys-alist))
282 avy-keys))
283 (avy-style (or (cdr (assq ',command avy-styles-alist))
284 avy-style)))
285 ,@body))
286
287 (defun avy--goto (x)
288 "Goto X.
289 X is (POS . WND)
290 POS is either a position or (BEG . END)."
291 (cond ((null x)
292 (message "zero candidates"))
293
294 ;; ignore exit from `avy-handler-function'
295 ((eq x 'exit))
296
297 (t
298 (select-window (cdr x))
299 (let ((pt (car x)))
300 (when (consp pt)
301 (setq pt (car pt)))
302 (unless (= pt (point)) (push-mark))
303 (goto-char pt)))))
304
305 (defun avy--process (candidates overlay-fn)
306 "Select one of CANDIDATES using `avy-read'.
307 Use OVERLAY-FN to visualize the decision overlay."
308 (unwind-protect
309 (cl-case (length candidates)
310 (0
311 nil)
312 (1
313 (car candidates))
314 (t
315 (avy--make-backgrounds
316 (avy-window-list))
317 (avy-read (avy-tree candidates avy-keys)
318 overlay-fn
319 #'avy--remove-leading-chars)))
320 (avy--done)))
321
322 (defvar avy--overlays-back nil
323 "Hold overlays for when `avy-background' is t.")
324
325 (defun avy--make-backgrounds (wnd-list)
326 "Create a dim background overlay for each window on WND-LIST."
327 (when avy-background
328 (setq avy--overlays-back
329 (mapcar (lambda (w)
330 (let ((ol (make-overlay
331 (window-start w)
332 (window-end w)
333 (window-buffer w))))
334 (overlay-put ol 'face 'avy-background-face)
335 (overlay-put ol 'window w)
336 ol))
337 wnd-list))))
338
339 (defun avy--done ()
340 "Clean up overlays."
341 (mapc #'delete-overlay avy--overlays-back)
342 (setq avy--overlays-back nil)
343 (avy--remove-leading-chars))
344
345 (defun avy--regex-candidates (regex &optional beg end pred group)
346 "Return all elements that match REGEX.
347 Each element of the list is ((BEG . END) . WND)
348 When PRED is non-nil, it's a filter for matching point positions.
349 When GROUP is non-nil, (BEG . END) should delimit that regex group."
350 (setq group (or group 0))
351 (let ((case-fold-search avy-case-fold-search)
352 candidates)
353 (avy-dowindows nil
354 (let ((we (or end (window-end (selected-window) t))))
355 (save-excursion
356 (goto-char (or beg (window-start)))
357 (while (re-search-forward regex we t)
358 (unless (get-char-property (point) 'invisible)
359 (when (or (null pred)
360 (funcall pred))
361 (push (cons (cons (match-beginning group)
362 (match-end group))
363 wnd) candidates)))))))
364 (nreverse candidates)))
365
366 (defvar avy--overlay-offset 0
367 "The offset to apply in `avy--overlay'.")
368
369 (defvar avy--overlays-lead nil
370 "Hold overlays for leading chars.")
371
372 (defun avy--remove-leading-chars ()
373 "Remove leading char overlays."
374 (mapc #'delete-overlay avy--overlays-lead)
375 (setq avy--overlays-lead nil))
376
377 (defun avy--overlay (str pt wnd)
378 "Create an overlay with STR at PT in WND."
379 (when (<= (1+ pt) (with-selected-window wnd (point-max)))
380 (let* ((pt (+ pt avy--overlay-offset))
381 (ol (make-overlay pt (1+ pt) (window-buffer wnd)))
382 (old-str (with-selected-window wnd
383 (buffer-substring pt (1+ pt)))))
384 (when avy-background
385 (setq old-str (propertize
386 old-str 'face 'avy-background-face)))
387 (overlay-put ol 'window wnd)
388 (overlay-put ol 'display (concat str old-str))
389 (push ol avy--overlays-lead))))
390
391 (defcustom avy-highlight-first nil
392 "When non-nil highlight the first decision char with `avy-lead-face-0'.
393 Do this even when the char is terminating."
394 :type 'boolean)
395
396 (defun avy--overlay-pre (path leaf)
397 "Create an overlay with PATH at LEAF.
398 PATH is a list of keys from tree root to LEAF.
399 LEAF is normally ((BEG . END) . WND)."
400 (let ((str (propertize (apply #'string (reverse path))
401 'face 'avy-lead-face)))
402 (when (or avy-highlight-first (> (length str) 1))
403 (set-text-properties 0 1 '(face avy-lead-face-0) str))
404 (setq str (concat
405 (propertize avy-current-path
406 'face 'avy-lead-face-1)
407 str))
408 (avy--overlay
409 str
410 (cond ((numberp leaf)
411 leaf)
412 ((consp (car leaf))
413 (caar leaf))
414 (t
415 (car leaf)))
416 (if (consp leaf)
417 (cdr leaf)
418 (selected-window)))))
419
420 (defun avy--overlay-at (path leaf)
421 "Create an overlay with PATH at LEAF.
422 PATH is a list of keys from tree root to LEAF.
423 LEAF is normally ((BEG . END) . WND)."
424 (let ((str (propertize
425 (string (car (last path)))
426 'face 'avy-lead-face))
427 (pt (+ (if (consp (car leaf))
428 (caar leaf)
429 (car leaf))
430 avy--overlay-offset))
431 (wnd (cdr leaf)))
432 (let ((ol (make-overlay pt (1+ pt)
433 (window-buffer wnd)))
434 (old-str (with-selected-window wnd
435 (buffer-substring pt (1+ pt)))))
436 (when avy-background
437 (setq old-str (propertize
438 old-str 'face 'avy-background-face)))
439 (overlay-put ol 'window wnd)
440 (overlay-put ol 'display (if (string= old-str "\n")
441 (concat str "\n")
442 str))
443 (push ol avy--overlays-lead))))
444
445 (defun avy--overlay-at-full (path leaf)
446 "Create an overlay with PATH at LEAF.
447 PATH is a list of keys from tree root to LEAF.
448 LEAF is normally ((BEG . END) . WND)."
449 (let* ((str (propertize
450 (apply #'string (reverse path))
451 'face 'avy-lead-face))
452 (len (length path))
453 (beg (if (consp (car leaf))
454 (caar leaf)
455 (car leaf)))
456 (wnd (cdr leaf)))
457 (when (or avy-highlight-first (> (length str) 1))
458 (set-text-properties 0 1 '(face avy-lead-face-0) str))
459 (with-selected-window wnd
460 (save-excursion
461 (goto-char beg)
462 (when (cl-some (lambda (o)
463 (and (eq (overlay-get o 'category) 'avy)
464 (eq (overlay-get o 'window) wnd)))
465 (overlays-in (point) (min (+ (point) len)
466 (line-end-position))))
467 (setq str (substring str 0 1))
468 (setq len 1))
469 (let ((other-ov (cl-find-if
470 (lambda (o)
471 (and (eq (overlay-get o 'category) 'avy)
472 (eq (overlay-start o) beg)
473 (not (eq (overlay-get o 'window) wnd))))
474 (overlays-in (point) (min (+ (point) len)
475 (line-end-position))))))
476 (when (and other-ov
477 (> (overlay-end other-ov)
478 (+ beg len)))
479 (setq str (concat str (buffer-substring
480 (+ beg len)
481 (overlay-end other-ov))))
482 (setq len (- (overlay-end other-ov)
483 beg))))
484 (let* ((end (if (= beg (line-end-position))
485 (1+ beg)
486 (min (+ beg
487 (if (eq (char-after) ?\t)
488 1
489 len))
490 (line-end-position))))
491 (ol (make-overlay
492 beg end
493 (current-buffer)))
494 (old-str (buffer-substring beg (1+ beg))))
495 (when avy-background
496 (setq old-str (propertize
497 old-str 'face 'avy-background-face)))
498 (overlay-put ol 'window wnd)
499 (overlay-put ol 'category 'avy)
500 (overlay-put ol 'display
501 (cond ((string= old-str "\n")
502 (concat str "\n"))
503 ((string= old-str "\t")
504 (concat str (make-string (- tab-width len) ?\ )))
505 (t
506 str)))
507 (push ol avy--overlays-lead))))))
508
509 (defun avy--overlay-post (path leaf)
510 "Create an overlay with PATH at LEAF.
511 PATH is a list of keys from tree root to LEAF.
512 LEAF is normally ((BEG . END) . WND)."
513 (let ((str (propertize (apply #'string (reverse path))
514 'face 'avy-lead-face)))
515 (when (or avy-highlight-first (> (length str) 1))
516 (set-text-properties 0 1 '(face avy-lead-face-0) str))
517 (setq str (concat
518 (propertize avy-current-path
519 'face 'avy-lead-face-1)
520 str))
521 (avy--overlay
522 str
523 (cond ((numberp leaf)
524 leaf)
525 ((consp (car leaf))
526 (cdar leaf))
527 (t
528 (car leaf)))
529 (if (consp leaf)
530 (cdr leaf)
531 (selected-window)))))
532
533 (defun avy--style-fn (style)
534 "Transform STYLE symbol to a style function."
535 (cl-case style
536 (pre #'avy--overlay-pre)
537 (at #'avy--overlay-at)
538 (at-full 'avy--overlay-at-full)
539 (post #'avy--overlay-post)
540 (t (error "Unexpected style %S" style))))
541
542 (defun avy--generic-jump (regex window-flip style)
543 "Jump to REGEX.
544 When WINDOW-FLIP is non-nil, do the opposite of `avy-all-windows'.
545 STYLE determines the leading char overlay style."
546 (let ((avy-all-windows
547 (if window-flip
548 (not avy-all-windows)
549 avy-all-windows)))
550 (avy--goto
551 (avy--process
552 (avy--regex-candidates regex)
553 (avy--style-fn style)))))
554
555 ;;* Commands
556 ;;;###autoload
557 (defun avy-goto-char (char &optional arg)
558 "Jump to the currently visible CHAR.
559 The window scope is determined by `avy-all-windows' (ARG negates it)."
560 (interactive (list (read-char "char: ")
561 current-prefix-arg))
562 (avy--with-avy-keys avy-goto-char
563 (avy--generic-jump
564 (if (= 13 char)
565 "\n"
566 (regexp-quote (string char)))
567 arg
568 avy-style)))
569
570 ;;;###autoload
571 (defun avy-goto-char-2 (char1 char2 &optional arg)
572 "Jump to the currently visible CHAR1 followed by CHAR2.
573 The window scope is determined by `avy-all-windows' (ARG negates it)."
574 (interactive (list (read-char "char 1: ")
575 (read-char "char 2: ")
576 current-prefix-arg))
577 (avy--with-avy-keys avy-goto-char-2
578 (avy--generic-jump
579 (regexp-quote (string char1 char2))
580 arg
581 avy-style)))
582
583 ;;;###autoload
584 (defun avy-isearch ()
585 "Jump to one of the current isearch candidates."
586 (interactive)
587 (avy--with-avy-keys avy-isearch
588 (let* ((candidates
589 (avy--regex-candidates isearch-string))
590 (avy-background nil)
591 (candidate
592 (avy--process candidates #'avy--overlay-post)))
593 (isearch-done)
594 (avy--goto candidate))))
595
596 ;;;###autoload
597 (defun avy-goto-word-0 (arg)
598 "Jump to a word start.
599 The window scope is determined by `avy-all-windows' (ARG negates it)."
600 (interactive "P")
601 (avy--with-avy-keys avy-goto-word-0
602 (avy--generic-jump "\\b\\sw" arg avy-style)))
603
604 ;;;###autoload
605 (defun avy-goto-word-1 (char &optional arg)
606 "Jump to the currently visible CHAR at a word start.
607 The window scope is determined by `avy-all-windows' (ARG negates it)."
608 (interactive (list (read-char "char: ")
609 current-prefix-arg))
610 (avy--with-avy-keys avy-goto-word-1
611 (let* ((str (string char))
612 (regex (cond ((string= str ".")
613 "\\.")
614 ((and avy-word-punc-regexp
615 (string-match avy-word-punc-regexp str))
616 str)
617 (t
618 (concat
619 "\\b"
620 str)))))
621 (avy--generic-jump regex arg avy-style))))
622
623 (declare-function subword-backward "subword")
624
625 ;;;###autoload
626 (defun avy-goto-subword-0 (&optional arg predicate)
627 "Jump to a word or subword start.
628
629 The window scope is determined by `avy-all-windows' (ARG negates it).
630
631 When PREDICATE is non-nil it's a function of zero parameters that
632 should return true."
633 (interactive "P")
634 (require 'subword)
635 (avy--with-avy-keys avy-goto-subword-0
636 (let ((case-fold-search nil)
637 candidates)
638 (avy-dowindows arg
639 (let ((ws (window-start))
640 window-cands)
641 (save-excursion
642 (goto-char (window-end (selected-window) t))
643 (subword-backward)
644 (while (> (point) ws)
645 (when (or (null predicate)
646 (and predicate (funcall predicate)))
647 (push (cons (point) (selected-window)) window-cands))
648 (subword-backward)))
649 (setq candidates (nconc candidates window-cands))))
650 (avy--goto
651 (avy--process candidates (avy--style-fn avy-style))))))
652
653 ;;;###autoload
654 (defun avy-goto-subword-1 (char arg)
655 "Jump to the currently visible CHAR at a subword start.
656 The window scope is determined by `avy-all-windows' (ARG negates it).
657 The case of CHAR is ignored."
658 (interactive (list (read-char "char: ")
659 current-prefix-arg))
660 (avy--with-avy-keys avy-goto-subword-1
661 (let ((char (downcase char)))
662 (avy-goto-subword-0
663 arg (lambda () (eq (downcase (char-after)) char))))))
664
665 (defun avy-goto-word-or-subword-1 ()
666 "Forward to `avy-goto-subword-1' or `avy-goto-word-1'.
667 Which one depends on variable `subword-mode'."
668 (interactive)
669 (if (bound-and-true-p subword-mode)
670 (call-interactively #'avy-goto-subword-1)
671 (call-interactively #'avy-goto-word-1)))
672
673 (defun avy--line (&optional arg)
674 "Select a line.
675 The window scope is determined by `avy-all-windows' (ARG negates it)."
676 (let ((avy-background nil)
677 candidates)
678 (avy-dowindows arg
679 (let ((ws (window-start)))
680 (save-excursion
681 (save-restriction
682 (narrow-to-region ws (window-end (selected-window) t))
683 (goto-char (point-min))
684 (while (< (point) (point-max))
685 (unless (get-char-property
686 (max (1- (point)) ws) 'invisible)
687 (push (cons
688 (if (eq avy-style 'post)
689 (line-end-position)
690 (line-beginning-position))
691 (selected-window)) candidates))
692 (forward-line 1))))))
693 (avy--process (nreverse candidates) (avy--style-fn avy-style))))
694
695 ;;;###autoload
696 (defun avy-goto-line (&optional arg)
697 "Jump to a line start in current buffer.
698 The window scope is determined by `avy-all-windows' (ARG negates it)."
699 (interactive "P")
700 (avy--with-avy-keys avy-goto-line
701 (let ((avy-handler-function
702 (lambda (char)
703 (if (or (< char ?0)
704 (> char ?9))
705 (avy-handler-default char)
706 (let ((line (read-from-minibuffer
707 "Goto line: " (string char))))
708 (when line
709 (goto-char (point-min))
710 (forward-line (1- (string-to-number line)))
711 (throw 'done 'exit)))))))
712 (avy--goto (avy--line arg)))))
713
714 ;;;###autoload
715 (defun avy-copy-line (arg)
716 "Copy a selected line above the current line.
717 ARG lines can be used."
718 (interactive "p")
719 (avy--with-avy-keys avy-copy-line
720 (let ((start (car (avy--line))))
721 (move-beginning-of-line nil)
722 (save-excursion
723 (insert
724 (buffer-substring-no-properties
725 start
726 (save-excursion
727 (goto-char start)
728 (move-end-of-line arg)
729 (point)))
730 "\n")))))
731
732 ;;;###autoload
733 (defun avy-move-line (arg)
734 "Move a selected line above the current line.
735 ARG lines can be used."
736 (interactive "p")
737 (avy--with-avy-keys avy-move-line
738 (let ((start (car (avy--line))))
739 (move-beginning-of-line nil)
740 (save-excursion
741 (save-excursion
742 (goto-char start)
743 (kill-whole-line arg))
744 (insert
745 (current-kill 0))))))
746
747 ;;;###autoload
748 (defun avy-copy-region ()
749 "Select two lines and copy the text between them here."
750 (interactive)
751 (avy--with-avy-keys avy-copy-region
752 (let ((beg (car (avy--line)))
753 (end (car (avy--line)))
754 (pad (if (bolp) "" "\n")))
755 (move-beginning-of-line nil)
756 (save-excursion
757 (insert
758 (buffer-substring-no-properties
759 beg
760 (save-excursion
761 (goto-char end)
762 (line-end-position)))
763 pad)))))
764
765 ;;;###autoload
766 (defun avy-setup-default ()
767 "Setup the default shortcuts."
768 (eval-after-load "isearch"
769 '(define-key isearch-mode-map (kbd "C-'") 'avy-isearch)))
770
771 (defcustom avy-timeout-seconds 0.5
772 "How many seconds to wait for the second char.")
773
774 ;;;###autoload
775 (defun avy-goto-char-timer (&optional arg)
776 "Read one or two consecutive chars and jump to the first one.
777 The window scope is determined by `avy-all-windows' (ARG negates it)."
778 (interactive "P")
779 (let ((c1 (read-char "char 1: "))
780 (c2 (read-char "char 2: " nil avy-timeout-seconds)))
781 (avy--generic-jump
782 (regexp-quote
783 (if c2
784 (string c1 c2)
785 (string c1)))
786 arg
787 avy-style)))
788
789 (define-obsolete-variable-alias
790 'avy-goto-char-style 'avy-style "0.1.0"
791 "Use `avy-style' and `avy-styles-alist' instead.")
792 (define-obsolete-variable-alias
793 'avy-goto-word-style 'avy-style "0.1.0"
794 "Use `avy-style' and `avy-styles-alist' instead.")
795 (define-obsolete-variable-alias 'avi-keys 'avy-keys "0.1.0")
796 (define-obsolete-variable-alias 'avi-background 'avy-background "0.1.0")
797 (define-obsolete-variable-alias 'avi-word-punc-regexp 'avy-word-punc-regexp "0.1.0")
798 (define-obsolete-face-alias 'avi-lead-face 'avy-lead-face "0.1.0")
799 (define-obsolete-function-alias 'avi--goto 'avy--goto "0.1.0")
800 (define-obsolete-function-alias 'avi--process 'avy--process "0.1.0")
801 (define-obsolete-variable-alias 'avi-all-windows 'avy-all-windows "0.1.0")
802 (define-obsolete-function-alias 'avi--overlay-pre 'avy--overlay-pre "0.1.0")
803 (define-obsolete-function-alias 'avi--overlay-at 'avy--overlay-at "0.1.0")
804 (define-obsolete-function-alias 'avi--overlay-post 'avy--overlay-post "0.1.0")
805 (define-obsolete-function-alias 'avi-goto-char 'avy-goto-char "0.1.0")
806 (define-obsolete-function-alias 'avi-goto-char-2 'avy-goto-char-2 "0.1.0")
807 (define-obsolete-function-alias 'avi-isearch 'avy-isearch "0.1.0")
808 (define-obsolete-function-alias 'avi-goto-word-0 'avy-goto-word-0 "0.1.0")
809 (define-obsolete-function-alias 'avi-goto-subword-0 'avy-goto-subword-0 "0.1.0")
810 (define-obsolete-function-alias 'avi-goto-word-1 'avy-goto-word-1 "0.1.0")
811 (define-obsolete-function-alias 'avi-goto-line 'avy-goto-line "0.1.0")
812 (define-obsolete-function-alias 'avi-copy-line 'avy-copy-line "0.1.0")
813 (define-obsolete-function-alias 'avi-move-line 'avy-move-line "0.1.0")
814 (define-obsolete-function-alias 'avi-copy-region 'avy-copy-region "0.1.0")
815 (define-obsolete-function-alias 'avi--regex-candidates 'avy--regex-candidates "0.1.0")
816
817 (provide 'avy)
818
819 ;;; avy.el ends here