Skip to content

Commit 90fc6ae

Browse files
aaronjensenabo-abo
authored andcommitted
Move lispy-occur to lispy-occur.el
Ensures that swiper and ivy only load on demand. Fixes #616
1 parent dea1c71 commit 90fc6ae

File tree

2 files changed

+216
-192
lines changed

2 files changed

+216
-192
lines changed

lispy-occur.el

+216
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
;;; lispy-occur.el --- Select a line within the current top level sexp. -*- lexical-binding: t -*-
2+
3+
;; Copyright (C) 2014-2021 Oleh Krehel
4+
5+
;; This file is not part of GNU Emacs
6+
7+
;; This file is free software; you can redistribute it and/or modify
8+
;; it under the terms of the GNU General Public License as published by
9+
;; the Free Software Foundation; either version 3, or (at your option)
10+
;; any later version.
11+
12+
;; This program is distributed in the hope that it will be useful,
13+
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
;; GNU General Public License for more details.
16+
17+
;; For a full copy of the GNU General Public License
18+
;; see <http://www.gnu.org/licenses/>.
19+
20+
;;; Commentary:
21+
;;
22+
23+
;;; Code:
24+
(require 'swiper)
25+
26+
(defcustom lispy-occur-backend 'ivy
27+
"Method to navigate to a line with `lispy-occur'."
28+
:type '(choice
29+
(const :tag "Ivy" ivy)
30+
(const :tag "Helm" helm)))
31+
32+
(defvar lispy--occur-beg 1
33+
"Start position of the top level sexp during `lispy-occur'.")
34+
35+
(defvar lispy--occur-end 1
36+
"End position of the top level sexp during `lispy-occur'.")
37+
38+
(defun lispy--occur-candidates (&optional bnd)
39+
"Return the candidates for `lispy-occur'."
40+
(setq bnd (or bnd (save-excursion
41+
(unless (and (bolp)
42+
(lispy-left-p))
43+
(beginning-of-defun))
44+
(lispy--bounds-dwim))))
45+
(let ((line-number -1)
46+
candidates)
47+
(setq lispy--occur-beg (car bnd))
48+
(setq lispy--occur-end (cdr bnd))
49+
(save-excursion
50+
(goto-char lispy--occur-beg)
51+
(while (< (point) lispy--occur-end)
52+
(push (format "%-3d %s"
53+
(cl-incf line-number)
54+
(buffer-substring
55+
(line-beginning-position)
56+
(line-end-position)))
57+
candidates)
58+
(forward-line 1)))
59+
(nreverse candidates)))
60+
61+
(defun lispy--occur-preselect ()
62+
"Initial candidate regex for `lispy-occur'."
63+
(format "^%d"
64+
(-
65+
(line-number-at-pos (point))
66+
(line-number-at-pos lispy--occur-beg))))
67+
68+
(defvar helm-input)
69+
(declare-function helm "ext:helm")
70+
71+
(defun lispy-occur-action-goto-paren (x)
72+
"Goto line X for `lispy-occur'."
73+
(setq x (read x))
74+
(goto-char lispy--occur-beg)
75+
(let ((input (if (eq lispy-occur-backend 'helm)
76+
helm-input
77+
ivy-text))
78+
str-or-comment)
79+
(cond ((string= input "")
80+
(forward-line x)
81+
(back-to-indentation)
82+
(when (re-search-forward lispy-left (line-end-position) t)
83+
(goto-char (match-beginning 0))))
84+
85+
((setq str-or-comment
86+
(progn
87+
(forward-line x)
88+
(re-search-forward (ivy--regex input)
89+
(line-end-position) t)
90+
(lispy--in-string-or-comment-p)))
91+
(goto-char str-or-comment))
92+
93+
((re-search-backward lispy-left (line-beginning-position) t)
94+
(goto-char (match-beginning 0)))
95+
96+
((re-search-forward lispy-left (line-end-position) t)
97+
(goto-char (match-beginning 0)))
98+
99+
(t
100+
(back-to-indentation)))))
101+
102+
(defun lispy-occur-action-goto-end (x)
103+
"Goto line X for `lispy-occur'."
104+
(setq x (read x))
105+
(goto-char lispy--occur-beg)
106+
(forward-line x)
107+
(re-search-forward (ivy--regex ivy-text) (line-end-position) t))
108+
109+
(defun lispy-occur-action-goto-beg (x)
110+
"Goto line X for `lispy-occur'."
111+
(when (lispy-occur-action-goto-end x)
112+
(goto-char (match-beginning 0))))
113+
114+
(defun lispy-occur-action-mc (_x)
115+
"Make a fake cursor for each `lispy-occur' candidate."
116+
(let ((cands (nreverse ivy--old-cands))
117+
cand)
118+
(while (setq cand (pop cands))
119+
(goto-char lispy--occur-beg)
120+
(forward-line (read cand))
121+
(re-search-forward (ivy--regex ivy-text) (line-end-position) t)
122+
(when cands
123+
(mc/create-fake-cursor-at-point))))
124+
(multiple-cursors-mode 1))
125+
126+
(ivy-set-actions
127+
'lispy-occur
128+
'(("m" lispy-occur-action-mc "multiple-cursors")
129+
("j" lispy-occur-action-goto-beg "goto start")
130+
("k" lispy-occur-action-goto-end "goto end")))
131+
132+
(defvar ivy-last)
133+
(declare-function ivy-state-window "ext:ivy")
134+
135+
;;;###autoload
136+
(defun lispy-occur ()
137+
"Select a line within current top level sexp.
138+
See `lispy-occur-backend' for the selection back end."
139+
(interactive)
140+
(swiper--init)
141+
(cond ((eq lispy-occur-backend 'helm)
142+
(require 'helm)
143+
(add-hook 'helm-move-selection-after-hook
144+
#'lispy--occur-update-input-helm)
145+
(add-hook 'helm-update-hook
146+
#'lispy--occur-update-input-helm)
147+
(unwind-protect
148+
(helm :sources
149+
`((name . "this defun")
150+
(candidates . ,(lispy--occur-candidates))
151+
(action . lispy-occur-action-goto-paren)
152+
(match-strict .
153+
(lambda (x)
154+
(ignore-errors
155+
(string-match
156+
(ivy--regex helm-input) x)))))
157+
:preselect (lispy--occur-preselect)
158+
:buffer "*lispy-occur*")
159+
(swiper--cleanup)
160+
(remove-hook 'helm-move-selection-after-hook
161+
#'lispy--occur-update-input-helm)
162+
(remove-hook 'helm-update-hook
163+
#'lispy--occur-update-input-helm)))
164+
((eq lispy-occur-backend 'ivy)
165+
(unwind-protect
166+
(ivy-read "pattern: "
167+
(lispy--occur-candidates)
168+
:preselect (lispy--occur-preselect)
169+
:require-match t
170+
:update-fn (lambda ()
171+
(lispy--occur-update-input
172+
ivy-text
173+
(ivy-state-current ivy-last)))
174+
:action #'lispy-occur-action-goto-paren
175+
:caller 'lispy-occur)
176+
(swiper--cleanup)
177+
(when (null ivy-exit)
178+
(goto-char swiper--opoint))))
179+
(t
180+
(error "Bad `lispy-occur-backend': %S" lispy-occur-backend))))
181+
182+
(defun lispy--occur-update-input-helm ()
183+
"Update selection for `lispy-occur' using `helm' back end."
184+
(lispy--occur-update-input
185+
helm-input
186+
(buffer-substring-no-properties
187+
(line-beginning-position)
188+
(line-end-position))))
189+
190+
(defun lispy--occur-update-input (input str)
191+
"Update selection for `ivy-occur'.
192+
INPUT is the current input text.
193+
STR is the full current candidate."
194+
(swiper--cleanup)
195+
(let ((re (ivy--regex input))
196+
(num (if (string-match "^[0-9]+" str)
197+
(string-to-number (match-string 0 str))
198+
0)))
199+
(with-selected-window (ivy-state-window ivy-last)
200+
(goto-char lispy--occur-beg)
201+
(when (cl-plusp num)
202+
(forward-line num)
203+
(unless (<= (point) lispy--occur-end)
204+
(recenter)))
205+
(let ((ov (make-overlay (line-beginning-position)
206+
(1+ (line-end-position)))))
207+
(overlay-put ov 'face 'swiper-line-face)
208+
(overlay-put ov 'window (ivy-state-window ivy-last))
209+
(push ov swiper--overlays))
210+
(re-search-forward re (line-end-position) t)
211+
(swiper--add-overlays
212+
re
213+
lispy--occur-beg
214+
lispy--occur-end))))
215+
216+
;;; lispy-occur.el ends here

0 commit comments

Comments
 (0)