]> code.delx.au - gnu-emacs-elpa/blob - avy.el
Allow for all operations to work across frames
[gnu-emacs-elpa] / 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.0
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 (cl-case avy-all-windows
254 (all-frames
255 (cl-mapcan #'window-list (frame-list)))
256 (this-frame
257 (window-list))
258 (t
259 (list (selected-window)))))
260
261 (defmacro avy-dowindows (flip &rest body)
262 "Depending on FLIP and `avy-all-windows' run BODY in each or selected window."
263 (declare (indent 1)
264 (debug (form body)))
265 `(let ((avy-all-windows (if ,flip
266 (not avy-all-windows)
267 avy-all-windows)))
268 (dolist (wnd (avy-window-list))
269 (with-selected-window wnd
270 (unless (memq major-mode '(image-mode doc-view-mode))
271 ,@body)))))
272
273 (defmacro avy--with-avy-keys (command &rest body)
274 "Set `avy-keys' according to COMMAND and execute BODY."
275 (declare (indent 1)
276 (debug (form body)))
277 `(let ((avy-keys (or (cdr (assq ',command avy-keys-alist))
278 avy-keys))
279 (avy-style (or (cdr (assq ',command avy-styles-alist))
280 avy-style)))
281 ,@body))
282
283 (defun avy--goto (x)
284 "Goto X.
285 X is (POS . WND)
286 POS is either a position or (BEG . END)."
287 (cond ((null x)
288 (message "zero candidates"))
289
290 ;; ignore exit from `avy-handler-function'
291 ((eq x 'exit))
292
293 (t
294 (select-window (cdr x))
295 (let ((pt (car x)))
296 (when (consp pt)
297 (setq pt (car pt)))
298 (unless (= pt (point)) (push-mark))
299 (goto-char pt)))))
300
301 (defun avy--process (candidates overlay-fn)
302 "Select one of CANDIDATES using `avy-read'.
303 Use OVERLAY-FN to visualize the decision overlay."
304 (unwind-protect
305 (cl-case (length candidates)
306 (0
307 nil)
308 (1
309 (car candidates))
310 (t
311 (avy--make-backgrounds
312 (avy-window-list))
313 (avy-read (avy-tree candidates avy-keys)
314 overlay-fn
315 #'avy--remove-leading-chars)))
316 (avy--done)))
317
318 (defvar avy--overlays-back nil
319 "Hold overlays for when `avy-background' is t.")
320
321 (defun avy--make-backgrounds (wnd-list)
322 "Create a dim background overlay for each window on WND-LIST."
323 (when avy-background
324 (setq avy--overlays-back
325 (mapcar (lambda (w)
326 (let ((ol (make-overlay
327 (window-start w)
328 (window-end w)
329 (window-buffer w))))
330 (overlay-put ol 'face 'avy-background-face)
331 (overlay-put ol 'window w)
332 ol))
333 wnd-list))))
334
335 (defun avy--done ()
336 "Clean up overlays."
337 (mapc #'delete-overlay avy--overlays-back)
338 (setq avy--overlays-back nil)
339 (avy--remove-leading-chars))
340
341 (defun avy--regex-candidates (regex &optional beg end pred group)
342 "Return all elements that match REGEX.
343 Each element of the list is ((BEG . END) . WND)
344 When PRED is non-nil, it's a filter for matching point positions.
345 When GROUP is non-nil, (BEG . END) should delimit that regex group."
346 (setq group (or group 0))
347 (let ((case-fold-search avy-case-fold-search)
348 candidates)
349 (avy-dowindows nil
350 (let ((we (or end (window-end (selected-window) t))))
351 (save-excursion
352 (goto-char (or beg (window-start)))
353 (while (re-search-forward regex we t)
354 (unless (get-char-property (point) 'invisible)
355 (when (or (null pred)
356 (funcall pred))
357 (push (cons (cons (match-beginning group)
358 (match-end group))
359 wnd) candidates)))))))
360 (nreverse candidates)))
361
362 (defvar avy--overlay-offset 0
363 "The offset to apply in `avy--overlay'.")
364
365 (defvar avy--overlays-lead nil
366 "Hold overlays for leading chars.")
367
368 (defun avy--remove-leading-chars ()
369 "Remove leading char overlays."
370 (mapc #'delete-overlay avy--overlays-lead)
371 (setq avy--overlays-lead nil))
372
373 (defun avy--overlay (str pt wnd)
374 "Create an overlay with STR at PT in WND."
375 (when (<= (1+ pt) (with-selected-window wnd (point-max)))
376 (let* ((pt (+ pt avy--overlay-offset))
377 (ol (make-overlay pt (1+ pt) (window-buffer wnd)))
378 (old-str (with-selected-window wnd
379 (buffer-substring pt (1+ pt)))))
380 (when avy-background
381 (setq old-str (propertize
382 old-str 'face 'avy-background-face)))
383 (overlay-put ol 'window wnd)
384 (overlay-put ol 'display (concat str old-str))
385 (push ol avy--overlays-lead))))
386
387 (defcustom avy-highlight-first nil
388 "When non-nil highlight the first decision char with `avy-lead-face-0'.
389 Do this even when the char is terminating."
390 :type 'boolean)
391
392 (defun avy--overlay-pre (path leaf)
393 "Create an overlay with PATH at LEAF.
394 PATH is a list of keys from tree root to LEAF.
395 LEAF is normally ((BEG . END) . WND)."
396 (let ((str (propertize (apply #'string (reverse path))
397 'face 'avy-lead-face)))
398 (when (or avy-highlight-first (> (length str) 1))
399 (set-text-properties 0 1 '(face avy-lead-face-0) str))
400 (setq str (concat
401 (propertize avy-current-path
402 'face 'avy-lead-face-1)
403 str))
404 (avy--overlay
405 str
406 (cond ((numberp leaf)
407 leaf)
408 ((consp (car leaf))
409 (caar leaf))
410 (t
411 (car leaf)))
412 (if (consp leaf)
413 (cdr leaf)
414 (selected-window)))))
415
416 (defun avy--overlay-at (path leaf)
417 "Create an overlay with PATH at LEAF.
418 PATH is a list of keys from tree root to LEAF.
419 LEAF is normally ((BEG . END) . WND)."
420 (let ((str (propertize
421 (string (car (last path)))
422 'face 'avy-lead-face))
423 (pt (+ (if (consp (car leaf))
424 (caar leaf)
425 (car leaf))
426 avy--overlay-offset))
427 (wnd (cdr leaf)))
428 (let ((ol (make-overlay pt (1+ pt)
429 (window-buffer wnd)))
430 (old-str (with-selected-window wnd
431 (buffer-substring pt (1+ pt)))))
432 (when avy-background
433 (setq old-str (propertize
434 old-str 'face 'avy-background-face)))
435 (overlay-put ol 'window wnd)
436 (overlay-put ol 'display (if (string= old-str "\n")
437 (concat str "\n")
438 str))
439 (push ol avy--overlays-lead))))
440
441 (defun avy--overlay-at-full (path leaf)
442 "Create an overlay with PATH at LEAF.
443 PATH is a list of keys from tree root to LEAF.
444 LEAF is normally ((BEG . END) . WND)."
445 (let* ((str (propertize
446 (apply #'string (reverse path))
447 'face 'avy-lead-face))
448 (len (length path))
449 (beg (if (consp (car leaf))
450 (caar leaf)
451 (car leaf)))
452 (wnd (cdr leaf)))
453 (when (or avy-highlight-first (> (length str) 1))
454 (set-text-properties 0 1 '(face avy-lead-face-0) str))
455 (with-selected-window wnd
456 (save-excursion
457 (goto-char beg)
458 (when (cl-some (lambda (o)
459 (eq (overlay-get o 'category) 'avy))
460 (overlays-in (point) (min (+ (point) len)
461 (line-end-position))))
462 (setq str (substring str 0 1))
463 (setq len 1))
464 (let* ((end (if (= beg (line-end-position))
465 (1+ beg)
466 (min (+ beg
467 (if (eq (char-after) ?\t)
468 1
469 len))
470 (line-end-position))))
471 (ol (make-overlay
472 beg end
473 (current-buffer)))
474 (old-str (buffer-substring beg (1+ beg))))
475 (when avy-background
476 (setq old-str (propertize
477 old-str 'face 'avy-background-face)))
478 (overlay-put ol 'window wnd)
479 (overlay-put ol 'category 'avy)
480 (overlay-put ol 'display
481 (cond ((string= old-str "\n")
482 (concat str "\n"))
483 ((string= old-str "\t")
484 (concat str (make-string (- tab-width len) ?\ )))
485 (t
486 str)))
487 (push ol avy--overlays-lead))))))
488
489 (defun avy--overlay-post (path leaf)
490 "Create an overlay with PATH at LEAF.
491 PATH is a list of keys from tree root to LEAF.
492 LEAF is normally ((BEG . END) . WND)."
493 (let ((str (propertize (apply #'string (reverse path))
494 'face 'avy-lead-face)))
495 (when (or avy-highlight-first (> (length str) 1))
496 (set-text-properties 0 1 '(face avy-lead-face-0) str))
497 (setq str (concat
498 (propertize avy-current-path
499 'face 'avy-lead-face-1)
500 str))
501 (avy--overlay
502 str
503 (cond ((numberp leaf)
504 leaf)
505 ((consp (car leaf))
506 (cdar leaf))
507 (t
508 (car leaf)))
509 (if (consp leaf)
510 (cdr leaf)
511 (selected-window)))))
512
513 (defun avy--style-fn (style)
514 "Transform STYLE symbol to a style function."
515 (cl-case style
516 (pre #'avy--overlay-pre)
517 (at #'avy--overlay-at)
518 (at-full 'avy--overlay-at-full)
519 (post #'avy--overlay-post)
520 (t (error "Unexpected style %S" style))))
521
522 (defun avy--generic-jump (regex window-flip style)
523 "Jump to REGEX.
524 When WINDOW-FLIP is non-nil, do the opposite of `avy-all-windows'.
525 STYLE determines the leading char overlay style."
526 (let ((avy-all-windows
527 (if window-flip
528 (not avy-all-windows)
529 avy-all-windows)))
530 (avy--goto
531 (avy--process
532 (avy--regex-candidates regex)
533 (avy--style-fn style)))))
534
535 ;;* Commands
536 ;;;###autoload
537 (defun avy-goto-char (char &optional arg)
538 "Jump to the currently visible CHAR.
539 The window scope is determined by `avy-all-windows' (ARG negates it)."
540 (interactive (list (read-char "char: ")
541 current-prefix-arg))
542 (avy--with-avy-keys avy-goto-char
543 (avy--generic-jump
544 (if (= 13 char)
545 "\n"
546 (regexp-quote (string char)))
547 arg
548 avy-style)))
549
550 ;;;###autoload
551 (defun avy-goto-char-2 (char1 char2 &optional arg)
552 "Jump to the currently visible CHAR1 followed by CHAR2.
553 The window scope is determined by `avy-all-windows' (ARG negates it)."
554 (interactive (list (read-char "char 1: ")
555 (read-char "char 2: ")
556 current-prefix-arg))
557 (avy--with-avy-keys avy-goto-char-2
558 (avy--generic-jump
559 (regexp-quote (string char1 char2))
560 arg
561 avy-style)))
562
563 ;;;###autoload
564 (defun avy-isearch ()
565 "Jump to one of the current isearch candidates."
566 (interactive)
567 (avy--with-avy-keys avy-isearch
568 (let* ((candidates
569 (avy--regex-candidates isearch-string))
570 (avy-background nil)
571 (candidate
572 (avy--process candidates #'avy--overlay-post)))
573 (isearch-done)
574 (avy--goto candidate))))
575
576 ;;;###autoload
577 (defun avy-goto-word-0 (arg)
578 "Jump to a word start.
579 The window scope is determined by `avy-all-windows' (ARG negates it)."
580 (interactive "P")
581 (avy--with-avy-keys avy-goto-word-0
582 (avy--generic-jump "\\b\\sw" arg avy-style)))
583
584 ;;;###autoload
585 (defun avy-goto-word-1 (char &optional arg)
586 "Jump to the currently visible CHAR at a word start.
587 The window scope is determined by `avy-all-windows' (ARG negates it)."
588 (interactive (list (read-char "char: ")
589 current-prefix-arg))
590 (avy--with-avy-keys avy-goto-word-1
591 (let* ((str (string char))
592 (regex (cond ((string= str ".")
593 "\\.")
594 ((and avy-word-punc-regexp
595 (string-match avy-word-punc-regexp str))
596 str)
597 (t
598 (concat
599 "\\b"
600 str)))))
601 (avy--generic-jump regex arg avy-style))))
602
603 (declare-function subword-backward "subword")
604
605 ;;;###autoload
606 (defun avy-goto-subword-0 (&optional arg predicate)
607 "Jump to a word or subword start.
608
609 The window scope is determined by `avy-all-windows' (ARG negates it).
610
611 When PREDICATE is non-nil it's a function of zero parameters that
612 should return true."
613 (interactive "P")
614 (require 'subword)
615 (avy--with-avy-keys avy-goto-subword-0
616 (let ((case-fold-search nil)
617 candidates)
618 (avy-dowindows arg
619 (let ((ws (window-start))
620 window-cands)
621 (save-excursion
622 (goto-char (window-end (selected-window) t))
623 (subword-backward)
624 (while (> (point) ws)
625 (when (or (null predicate)
626 (and predicate (funcall predicate)))
627 (push (cons (point) (selected-window)) window-cands))
628 (subword-backward)))
629 (setq candidates (nconc candidates window-cands))))
630 (avy--goto
631 (avy--process candidates (avy--style-fn avy-style))))))
632
633 ;;;###autoload
634 (defun avy-goto-subword-1 (char arg)
635 "Jump to the currently visible CHAR at a subword start.
636 The window scope is determined by `avy-all-windows' (ARG negates it).
637 The case of CHAR is ignored."
638 (interactive (list (read-char "char: ")
639 current-prefix-arg))
640 (avy--with-avy-keys avy-goto-subword-1
641 (let ((char (downcase char)))
642 (avy-goto-subword-0
643 arg (lambda () (eq (downcase (char-after)) char))))))
644
645 (defun avy-goto-word-or-subword-1 ()
646 "Forward to `avy-goto-subword-1' or `avy-goto-word-1'.
647 Which one depends on variable `subword-mode'."
648 (interactive)
649 (if (bound-and-true-p subword-mode)
650 (call-interactively #'avy-goto-subword-1)
651 (call-interactively #'avy-goto-word-1)))
652
653 (defun avy--line (&optional arg)
654 "Select a line.
655 The window scope is determined by `avy-all-windows' (ARG negates it)."
656 (let ((avy-background nil)
657 candidates)
658 (avy-dowindows arg
659 (let ((ws (window-start)))
660 (save-excursion
661 (save-restriction
662 (narrow-to-region ws (window-end (selected-window) t))
663 (goto-char (point-min))
664 (while (< (point) (point-max))
665 (unless (get-char-property
666 (max (1- (point)) ws) 'invisible)
667 (push (cons
668 (if (eq avy-style 'post)
669 (line-end-position)
670 (line-beginning-position))
671 (selected-window)) candidates))
672 (forward-line 1))))))
673 (avy--process (nreverse candidates) (avy--style-fn avy-style))))
674
675 ;;;###autoload
676 (defun avy-goto-line (&optional arg)
677 "Jump to a line start in current buffer.
678 The window scope is determined by `avy-all-windows' (ARG negates it)."
679 (interactive "P")
680 (avy--with-avy-keys avy-goto-line
681 (let ((avy-handler-function
682 (lambda (char)
683 (if (or (< char ?0)
684 (> char ?9))
685 (avy-handler-default char)
686 (let ((line (read-from-minibuffer
687 "Goto line: " (string char))))
688 (when line
689 (goto-char (point-min))
690 (forward-line (1- (string-to-number line)))
691 (throw 'done 'exit)))))))
692 (avy--goto (avy--line arg)))))
693
694 ;;;###autoload
695 (defun avy-copy-line (arg)
696 "Copy a selected line above the current line.
697 ARG lines can be used."
698 (interactive "p")
699 (avy--with-avy-keys avy-copy-line
700 (let ((start (car (avy--line))))
701 (move-beginning-of-line nil)
702 (save-excursion
703 (insert
704 (buffer-substring-no-properties
705 start
706 (save-excursion
707 (goto-char start)
708 (move-end-of-line arg)
709 (point)))
710 "\n")))))
711
712 ;;;###autoload
713 (defun avy-move-line (arg)
714 "Move a selected line above the current line.
715 ARG lines can be used."
716 (interactive "p")
717 (avy--with-avy-keys avy-move-line
718 (let ((start (car (avy--line))))
719 (move-beginning-of-line nil)
720 (save-excursion
721 (save-excursion
722 (goto-char start)
723 (kill-whole-line arg))
724 (insert
725 (current-kill 0))))))
726
727 ;;;###autoload
728 (defun avy-copy-region ()
729 "Select two lines and copy the text between them here."
730 (interactive)
731 (avy--with-avy-keys avy-copy-region
732 (let ((beg (car (avy--line)))
733 (end (car (avy--line)))
734 (pad (if (bolp) "" "\n")))
735 (move-beginning-of-line nil)
736 (save-excursion
737 (insert
738 (buffer-substring-no-properties
739 beg
740 (save-excursion
741 (goto-char end)
742 (line-end-position)))
743 pad)))))
744
745 ;;;###autoload
746 (defun avy-setup-default ()
747 "Setup the default shortcuts."
748 (eval-after-load "isearch"
749 '(define-key isearch-mode-map (kbd "C-'") 'avy-isearch)))
750
751 (defcustom avy-timeout-seconds 0.5
752 "How many seconds to wait for the second char.")
753
754 ;;;###autoload
755 (defun avy-goto-char-timer (&optional arg)
756 "Read one or two consecutive chars and jump to the first one.
757 The window scope is determined by `avy-all-windows' (ARG negates it)."
758 (interactive "P")
759 (let ((c1 (read-char "char 1: "))
760 (c2 (read-char "char 2: " nil avy-timeout-seconds)))
761 (avy--generic-jump
762 (regexp-quote
763 (if c2
764 (string c1 c2)
765 (string c1)))
766 arg
767 avy-style)))
768
769 (define-obsolete-variable-alias
770 'avy-goto-char-style 'avy-style "0.1.0"
771 "Use `avy-style' and `avy-styles-alist' instead.")
772 (define-obsolete-variable-alias
773 'avy-goto-word-style 'avy-style "0.1.0"
774 "Use `avy-style' and `avy-styles-alist' instead.")
775 (define-obsolete-variable-alias 'avi-keys 'avy-keys "0.1.0")
776 (define-obsolete-variable-alias 'avi-background 'avy-background "0.1.0")
777 (define-obsolete-variable-alias 'avi-word-punc-regexp 'avy-word-punc-regexp "0.1.0")
778 (define-obsolete-face-alias 'avi-lead-face 'avy-lead-face "0.1.0")
779 (define-obsolete-function-alias 'avi--goto 'avy--goto "0.1.0")
780 (define-obsolete-function-alias 'avi--process 'avy--process "0.1.0")
781 (define-obsolete-variable-alias 'avi-all-windows 'avy-all-windows "0.1.0")
782 (define-obsolete-function-alias 'avi--overlay-pre 'avy--overlay-pre "0.1.0")
783 (define-obsolete-function-alias 'avi--overlay-at 'avy--overlay-at "0.1.0")
784 (define-obsolete-function-alias 'avi--overlay-post 'avy--overlay-post "0.1.0")
785 (define-obsolete-function-alias 'avi-goto-char 'avy-goto-char "0.1.0")
786 (define-obsolete-function-alias 'avi-goto-char-2 'avy-goto-char-2 "0.1.0")
787 (define-obsolete-function-alias 'avi-isearch 'avy-isearch "0.1.0")
788 (define-obsolete-function-alias 'avi-goto-word-0 'avy-goto-word-0 "0.1.0")
789 (define-obsolete-function-alias 'avi-goto-subword-0 'avy-goto-subword-0 "0.1.0")
790 (define-obsolete-function-alias 'avi-goto-word-1 'avy-goto-word-1 "0.1.0")
791 (define-obsolete-function-alias 'avi-goto-line 'avy-goto-line "0.1.0")
792 (define-obsolete-function-alias 'avi-copy-line 'avy-copy-line "0.1.0")
793 (define-obsolete-function-alias 'avi-move-line 'avy-move-line "0.1.0")
794 (define-obsolete-function-alias 'avi-copy-region 'avy-copy-region "0.1.0")
795 (define-obsolete-function-alias 'avi--regex-candidates 'avy--regex-candidates "0.1.0")
796
797 (provide 'avy)
798
799 ;;; avy.el ends here