commit | author | age
|
76bbd0
|
1 |
;;; helm.el --- Emacs incremental and narrowing framework -*- lexical-binding: t -*- |
C |
2 |
|
|
3 |
;; Copyright (C) 2007 Tamas Patrovics |
|
4 |
;; 2008 ~ 2011 rubikitch <rubikitch@ruby-lang.org> |
|
5 |
;; 2011 ~ 2018 Thierry Volpiatto <thierry.volpiatto@gmail.com> |
|
6 |
|
|
7 |
;; This is a fork of anything.el wrote by Tamas Patrovics. |
|
8 |
|
|
9 |
;; Authors of anything.el: Tamas Patrovics |
|
10 |
;; rubikitch <rubikitch@ruby-lang.org> |
|
11 |
;; Thierry Volpiatto <thierry.volpiatto@gmail.com> |
|
12 |
|
|
13 |
;; Author: Thierry Volpiatto <thierry.volpiatto@gmail.com> |
|
14 |
;; URL: http://github.com/emacs-helm/helm |
|
15 |
|
|
16 |
;; This program is free software; you can redistribute it and/or modify |
|
17 |
;; it under the terms of the GNU General Public License as published by |
|
18 |
;; the Free Software Foundation, either version 3 of the License, or |
|
19 |
;; (at your option) any later version. |
|
20 |
|
|
21 |
;; This program is distributed in the hope that it will be useful, |
|
22 |
;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
23 |
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
24 |
;; GNU General Public License for more details. |
|
25 |
|
|
26 |
;; You should have received a copy of the GNU General Public License |
|
27 |
;; along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
28 |
|
|
29 |
;;; Code: |
|
30 |
|
|
31 |
(require 'cl-lib) |
|
32 |
(require 'async) |
|
33 |
(require 'advice) ; Shutup byte compiler about ad-deactivate. |
|
34 |
(require 'helm-lib) |
|
35 |
(require 'helm-multi-match) |
|
36 |
(require 'helm-source) |
|
37 |
|
|
38 |
|
|
39 |
;;; Multi keys |
|
40 |
;; |
|
41 |
;; |
|
42 |
;;;###autoload |
|
43 |
(defun helm-define-multi-key (keymap key functions &optional delay) |
|
44 |
"In KEYMAP, define key sequence KEY for function list FUNCTIONS. |
|
45 |
Each function runs sequentially for each KEY press. |
|
46 |
If DELAY is specified, switch back to initial function of FUNCTIONS list |
|
47 |
after DELAY seconds. |
|
48 |
The functions in FUNCTIONS list take no args. |
|
49 |
e.g |
|
50 |
(defun foo () |
|
51 |
(interactive) |
|
52 |
(message \"Run foo\")) |
|
53 |
(defun bar () |
|
54 |
(interactive) |
|
55 |
(message \"Run bar\")) |
|
56 |
(defun baz () |
|
57 |
(interactive) |
|
58 |
(message \"Run baz\")) |
|
59 |
|
|
60 |
\(helm-define-multi-key global-map (kbd \"<f5> q\") '(foo bar baz) 2) |
|
61 |
|
|
62 |
Each time \"<f5> q\" is pressed, the next function is executed. Waiting |
|
63 |
more than 2 seconds between key presses switches back to executing the first |
|
64 |
function on the next hit." |
|
65 |
(define-key keymap key (helm-make-multi-command functions delay))) |
|
66 |
|
|
67 |
;;;###autoload |
|
68 |
(defmacro helm-multi-key-defun (name docstring funs &optional delay) |
|
69 |
"Define NAME as a multi-key command running FUNS. |
|
70 |
After DELAY seconds, the FUNS list is reinitialized. |
|
71 |
See `helm-define-multi-key'." |
|
72 |
(declare (indent 2)) |
|
73 |
(setq docstring (if docstring (concat docstring "\n\n") |
|
74 |
"This is a helm-ish multi-key command.")) |
|
75 |
`(defalias (quote ,name) (helm-make-multi-command ,funs ,delay) ,docstring)) |
|
76 |
|
|
77 |
(defun helm-make-multi-command (functions &optional delay) |
|
78 |
"Return an anonymous multi-key command running FUNCTIONS. |
|
79 |
Run each function in the FUNCTIONS list in turn when called within DELAY seconds." |
|
80 |
(declare (indent 1)) |
|
81 |
(let ((funs functions) |
|
82 |
(iter (cl-gensym "helm-iter-key")) |
|
83 |
(timeout delay)) |
|
84 |
(eval (list 'defvar iter nil)) |
|
85 |
(lambda () |
|
86 |
(interactive) |
|
87 |
(helm-run-multi-key-command funs iter timeout)))) |
|
88 |
|
|
89 |
(defun helm-run-multi-key-command (functions iterator delay) |
|
90 |
(let ((fn (lambda () |
|
91 |
(cl-loop for count from 1 to (length functions) |
|
92 |
collect count))) |
|
93 |
next) |
|
94 |
(unless (and (symbol-value iterator) |
|
95 |
;; Reset iterator when another key is pressed. |
|
96 |
(eq this-command real-last-command)) |
|
97 |
(set iterator (helm-iter-list (funcall fn)))) |
|
98 |
(setq next (helm-iter-next (symbol-value iterator))) |
|
99 |
(unless next |
|
100 |
(set iterator (helm-iter-list (funcall fn))) |
|
101 |
(setq next (helm-iter-next (symbol-value iterator)))) |
|
102 |
(and next (symbol-value iterator) |
|
103 |
(call-interactively (nth (1- next) functions))) |
|
104 |
(when delay (run-with-idle-timer |
|
105 |
delay nil (lambda () |
|
106 |
(set iterator nil)))))) |
|
107 |
|
|
108 |
(helm-multi-key-defun helm-toggle-resplit-and-swap-windows |
|
109 |
"Multi key command to re-split and swap helm window. |
|
110 |
First call runs `helm-toggle-resplit-window', |
|
111 |
and second call within 0.5s runs `helm-swap-windows'." |
|
112 |
'(helm-toggle-resplit-window helm-swap-windows) 1) |
|
113 |
(put 'helm-toggle-resplit-and-swap-windows 'helm-only t) |
|
114 |
|
|
115 |
;;;###autoload |
|
116 |
(defun helm-define-key-with-subkeys (map key subkey command |
|
117 |
&optional other-subkeys prompt exit-fn) |
|
118 |
"Defines in MAP a KEY and SUBKEY to COMMAND. |
|
119 |
|
|
120 |
This allows typing KEY to call COMMAND the first time and |
|
121 |
type only SUBKEY on subsequent calls. |
|
122 |
|
|
123 |
Arg MAP is the keymap to use, SUBKEY is the initial short key-binding to |
|
124 |
call COMMAND. |
|
125 |
|
|
126 |
Arg OTHER-SUBKEYS is an alist specifying other short key-bindings |
|
127 |
to use once started e.g: |
|
128 |
|
|
129 |
\(helm-define-key-with-subkeys global-map |
|
130 |
\(kbd \"C-x v n\") ?n 'git-gutter:next-hunk '((?p . git-gutter:previous-hunk))\) |
|
131 |
|
|
132 |
|
|
133 |
In this example, `C-x v n' will run `git-gutter:next-hunk' |
|
134 |
subsequent \"n\"'s run this command again |
|
135 |
and subsequent \"p\"'s run `git-gutter:previous-hunk'. |
|
136 |
|
|
137 |
If specified PROMPT can be displayed in minibuffer to |
|
138 |
describe SUBKEY and OTHER-SUBKEYS. |
|
139 |
Arg EXIT-FN specifies a function to run on exit. |
|
140 |
|
|
141 |
For any other keys pressed, run their assigned command as defined |
|
142 |
in MAP and then exit the loop running EXIT-FN, if specified. |
|
143 |
|
|
144 |
NOTE: SUBKEY and OTHER-SUBKEYS bindings support only char syntax and |
|
145 |
vectors, so don't use strings to define them." |
|
146 |
(declare (indent 1)) |
|
147 |
(define-key map key |
|
148 |
(lambda () |
|
149 |
(interactive) |
|
150 |
(unwind-protect |
|
151 |
(progn |
|
152 |
(call-interactively command) |
|
153 |
(while (let ((input (read-key prompt)) other kb com) |
|
154 |
(setq last-command-event input) |
|
155 |
(cond |
|
156 |
((eq input subkey) |
|
157 |
(call-interactively command) |
|
158 |
t) |
|
159 |
((setq other (assoc input other-subkeys)) |
|
160 |
(call-interactively (cdr other)) |
|
161 |
t) |
|
162 |
(t |
|
163 |
(setq kb (vector last-command-event)) |
|
164 |
(setq com (lookup-key map kb)) |
|
165 |
(if (commandp com) |
|
166 |
(call-interactively com) |
|
167 |
(setq unread-command-events |
|
168 |
(nconc (mapcar 'identity kb) |
|
169 |
unread-command-events))) |
|
170 |
nil))))) |
|
171 |
(and exit-fn (funcall exit-fn)))))) |
|
172 |
|
|
173 |
;;; Keymap |
|
174 |
;; |
|
175 |
;; |
|
176 |
(defvar helm-map |
|
177 |
(let ((map (make-sparse-keymap))) |
|
178 |
(set-keymap-parent map minibuffer-local-map) |
|
179 |
(define-key map (kbd "<down>") 'helm-next-line) |
|
180 |
(define-key map (kbd "<up>") 'helm-previous-line) |
|
181 |
(define-key map (kbd "C-n") 'helm-next-line) |
|
182 |
(define-key map (kbd "C-p") 'helm-previous-line) |
|
183 |
(define-key map (kbd "<C-down>") 'helm-follow-action-forward) |
|
184 |
(define-key map (kbd "<C-up>") 'helm-follow-action-backward) |
|
185 |
(define-key map (kbd "<prior>") 'helm-previous-page) |
|
186 |
(define-key map (kbd "<next>") 'helm-next-page) |
|
187 |
(define-key map (kbd "M-v") 'helm-previous-page) |
|
188 |
(define-key map (kbd "C-v") 'helm-next-page) |
|
189 |
(define-key map (kbd "M-<") 'helm-beginning-of-buffer) |
|
190 |
(define-key map (kbd "M->") 'helm-end-of-buffer) |
|
191 |
(define-key map (kbd "C-g") 'helm-keyboard-quit) |
|
192 |
(define-key map (kbd "<right>") 'helm-next-source) |
|
193 |
(define-key map (kbd "<left>") 'helm-previous-source) |
|
194 |
(define-key map (kbd "<RET>") 'helm-maybe-exit-minibuffer) |
|
195 |
(define-key map (kbd "C-i") 'helm-select-action) |
|
196 |
(define-key map (kbd "C-z") 'helm-execute-persistent-action) |
|
197 |
(define-key map (kbd "C-j") 'helm-execute-persistent-action) |
|
198 |
(define-key map (kbd "C-o") 'helm-next-source) |
|
199 |
(define-key map (kbd "M-o") 'helm-previous-source) |
|
200 |
(define-key map (kbd "C-l") 'helm-recenter-top-bottom-other-window) |
|
201 |
(define-key map (kbd "M-C-l") 'helm-reposition-window-other-window) |
|
202 |
(define-key map (kbd "C-M-v") 'helm-scroll-other-window) |
|
203 |
(define-key map (kbd "M-<next>") 'helm-scroll-other-window) |
|
204 |
(define-key map (kbd "C-M-y") 'helm-scroll-other-window-down) |
|
205 |
(define-key map (kbd "C-M-S-v") 'helm-scroll-other-window-down) |
|
206 |
(define-key map (kbd "M-<prior>") 'helm-scroll-other-window-down) |
|
207 |
(define-key map (kbd "<C-M-down>") 'helm-scroll-other-window) |
|
208 |
(define-key map (kbd "<C-M-up>") 'helm-scroll-other-window-down) |
|
209 |
(define-key map (kbd "C-@") 'helm-toggle-visible-mark) |
|
210 |
(define-key map (kbd "C-SPC") 'helm-toggle-visible-mark) |
|
211 |
(define-key map (kbd "M-SPC") 'helm-toggle-visible-mark) |
|
212 |
(define-key map (kbd "M-[") nil) |
|
213 |
(define-key map (kbd "M-(") 'helm-prev-visible-mark) |
|
214 |
(define-key map (kbd "M-)") 'helm-next-visible-mark) |
|
215 |
(define-key map (kbd "C-k") 'helm-delete-minibuffer-contents) |
|
216 |
(define-key map (kbd "C-x C-f") 'helm-quit-and-find-file) |
|
217 |
(define-key map (kbd "M-m") 'helm-toggle-all-marks) |
|
218 |
(define-key map (kbd "M-a") 'helm-mark-all) |
|
219 |
(define-key map (kbd "M-U") 'helm-unmark-all) |
|
220 |
(define-key map (kbd "C-M-a") 'helm-show-all-in-this-source-only) |
|
221 |
(define-key map (kbd "C-M-e") 'helm-display-all-sources) |
|
222 |
(define-key map (kbd "C-s") 'undefined) |
|
223 |
(define-key map (kbd "M-s") 'undefined) |
|
224 |
(define-key map (kbd "C-}") 'helm-narrow-window) |
|
225 |
(define-key map (kbd "C-{") 'helm-enlarge-window) |
|
226 |
(define-key map (kbd "C-c -") 'helm-swap-windows) |
|
227 |
(define-key map (kbd "C-c _") 'helm-toggle-full-frame) |
|
228 |
(define-key map (kbd "C-c %") 'helm-exchange-minibuffer-and-header-line) |
|
229 |
(define-key map (kbd "C-c C-y") 'helm-yank-selection) |
|
230 |
(define-key map (kbd "C-c C-k") 'helm-kill-selection-and-quit) |
|
231 |
(define-key map (kbd "C-c C-i") 'helm-copy-to-buffer) |
|
232 |
(define-key map (kbd "C-c C-f") 'helm-follow-mode) |
|
233 |
(define-key map (kbd "C-c C-u") 'helm-refresh) |
|
234 |
(define-key map (kbd "C-c >") 'helm-toggle-truncate-line) |
|
235 |
(define-key map (kbd "M-p") 'previous-history-element) |
|
236 |
(define-key map (kbd "M-n") 'next-history-element) |
|
237 |
(define-key map (kbd "C-!") 'helm-toggle-suspend-update) |
|
238 |
(define-key map (kbd "C-x b") 'helm-resume-previous-session-after-quit) |
|
239 |
(define-key map (kbd "C-x C-b") 'helm-resume-list-buffers-after-quit) |
|
240 |
(helm-define-key-with-subkeys map (kbd "C-c n") ?n 'helm-run-cycle-resume) |
|
241 |
;; Disable `file-cache-minibuffer-complete'. |
|
242 |
(define-key map (kbd "<C-tab>") 'undefined) |
|
243 |
;; Multi keys |
|
244 |
(define-key map (kbd "C-t") 'helm-toggle-resplit-and-swap-windows) |
|
245 |
;; Debugging command |
|
246 |
(define-key map (kbd "C-h C-d") 'undefined) |
|
247 |
(define-key map (kbd "C-h C-d") 'helm-enable-or-switch-to-debug) |
|
248 |
(define-key map (kbd "C-h c") 'helm-customize-group) |
|
249 |
;; Allow to eval keymap without errors. |
|
250 |
(define-key map [f1] nil) |
|
251 |
(define-key map (kbd "C-h C-h") 'undefined) |
|
252 |
(define-key map (kbd "C-h h") 'undefined) |
|
253 |
(helm-define-key-with-subkeys map |
|
254 |
(kbd "C-w") ?\C-w 'helm-yank-text-at-point |
|
255 |
'((?\C-_ . helm-undo-yank-text-at-point))) |
|
256 |
;; Use `describe-mode' key in `global-map'. |
|
257 |
(cl-dolist (k (where-is-internal 'describe-mode global-map)) |
|
258 |
(define-key map k 'helm-help)) |
|
259 |
(define-key map (kbd "C-c ?") 'helm-help) |
|
260 |
;; Bind all actions from 1 to 12 to their corresponding nth index+1. |
|
261 |
(cl-loop for n from 0 to 12 do |
|
262 |
(define-key map (kbd (format "<f%s>" (1+ n))) |
|
263 |
`(lambda () |
|
264 |
(interactive) |
|
265 |
(helm-select-nth-action ,n)))) |
|
266 |
map) |
|
267 |
"Keymap for helm.") |
|
268 |
|
|
269 |
(defun helm-customize-group () |
|
270 |
"Jump to customization group of current source. |
|
271 |
|
|
272 |
Default to `helm' when group is not defined in source." |
|
273 |
(interactive) |
|
274 |
(helm-run-after-exit 'customize-group (helm-attr 'group))) |
|
275 |
(put 'helm-customize-group 'helm-only t) |
|
276 |
|
|
277 |
(defun helm--action-at-nth-set-fn-1 (value &optional negative) |
|
278 |
(cl-loop for n from 1 to 9 |
|
279 |
for key = (format value n) |
|
280 |
for sym = (make-symbol (format "helm-execute-selection-action-at-nth-+%d" n)) |
|
281 |
for fn = `(lambda () |
|
282 |
(interactive) |
|
283 |
(helm-execute-selection-action-at-nth ,(if negative (- n) n))) |
|
284 |
do (progn |
|
285 |
(defalias sym fn) |
|
286 |
(define-key helm-map (kbd key) sym)))) |
|
287 |
|
|
288 |
(defun helm--action-at-nth-set-fn- (var val) |
|
289 |
(set var val) |
|
290 |
(helm--action-at-nth-set-fn-1 val 'negative)) |
|
291 |
|
|
292 |
(defun helm--action-at-nth-set-fn+ (var val) |
|
293 |
(set var val) |
|
294 |
(helm--action-at-nth-set-fn-1 val)) |
|
295 |
|
|
296 |
(defcustom helm-action-at-nth-negative-prefix-key "C-x %d" |
|
297 |
"The prefix key to execute default action on nth <-n> candidate. |
|
298 |
|
|
299 |
This is a format spec where %d will be replaced by the candidate |
|
300 |
number. |
|
301 |
|
|
302 |
NOTE: `setq' have no effect until you restart emacs, use customize for |
|
303 |
immediate effect." |
|
304 |
:group 'helm |
|
305 |
:type 'string |
|
306 |
:set #'helm--action-at-nth-set-fn-) |
|
307 |
|
|
308 |
(defcustom helm-action-at-nth-positive-prefix-key "C-c %d" |
|
309 |
"The prefix key to execute default action on nth <+n> candidate. |
|
310 |
|
|
311 |
This is a format spec where %d will be replaced by the candidate |
|
312 |
number. |
|
313 |
|
|
314 |
NOTE: `setq' have no effect until you restart emacs, use customize for |
|
315 |
immediate effect." |
|
316 |
:group 'helm |
|
317 |
:type 'string |
|
318 |
:set #'helm--action-at-nth-set-fn+) |
|
319 |
|
|
320 |
|
|
321 |
(defgroup helm nil |
|
322 |
"Open helm." |
|
323 |
:prefix "helm-" :group 'convenience) |
|
324 |
|
|
325 |
(defcustom helm-completion-window-scroll-margin 5 |
|
326 |
" `scroll-margin' to use for helm completion window. |
|
327 |
Set to 0 to disable. |
|
328 |
NOTE: This has no effect when `helm-display-source-at-screen-top' |
|
329 |
id is non-`nil'." |
|
330 |
:group 'helm |
|
331 |
:type 'integer) |
|
332 |
|
|
333 |
(defcustom helm-display-source-at-screen-top t |
|
334 |
"Display candidates at the top of screen. |
|
335 |
This happens with `helm-next-source' and `helm-previous-source'. |
|
336 |
NOTE: When non-`nil' (default), disable `helm-completion-window-scroll-margin'." |
|
337 |
:group 'helm |
|
338 |
:type 'boolean) |
|
339 |
|
|
340 |
(defcustom helm-candidate-number-limit 100 |
|
341 |
"Global limit for number of candidates displayed. |
|
342 |
When the pattern is empty, the number of candidates shown will be |
|
343 |
as set here instead of the entire list, which may be hundreds or |
|
344 |
thousands. Since narrowing and filtering rapidly reduces |
|
345 |
available candidates, having a small list will keep the interface |
|
346 |
responsive. |
|
347 |
|
|
348 |
Set this value to nil for no limit." |
|
349 |
:group 'helm |
|
350 |
:type '(choice (const :tag "Disabled" nil) integer)) |
|
351 |
|
|
352 |
(defcustom helm-input-idle-delay 0.01 |
|
353 |
"Idle time before updating, specified in seconds." |
|
354 |
:group 'helm |
|
355 |
:type 'float) |
|
356 |
|
|
357 |
(defcustom helm-exit-idle-delay 0 |
|
358 |
"Idle time before exiting minibuffer while helm is updating. |
|
359 |
Has no affect when helm-buffer is up to date \(i.e exit without |
|
360 |
delay in this condition\)." |
|
361 |
:group 'helm |
|
362 |
:type 'float) |
|
363 |
|
|
364 |
(defvaralias 'helm-samewindow 'helm-full-frame) |
|
365 |
(make-obsolete-variable 'helm-samewindow 'helm-full-frame "1.4.8.1") |
|
366 |
(defcustom helm-full-frame nil |
|
367 |
"Use current window for showing candidates. |
|
368 |
If t, then Helm does not pop-up new window." |
|
369 |
:group 'helm |
|
370 |
:type 'boolean) |
|
371 |
|
|
372 |
(defcustom helm-candidate-separator |
|
373 |
"--------------------" |
|
374 |
"Candidates separator of `multiline' source." |
|
375 |
:group 'helm |
|
376 |
:type 'string) |
|
377 |
|
|
378 |
(defcustom helm-save-configuration-functions |
|
379 |
'(set-window-configuration . current-window-configuration) |
|
380 |
"Functions used to restore or save configurations for frames and windows. |
|
381 |
Specified as a pair of functions, where car is the restore function and cdr |
|
382 |
is the save function. |
|
383 |
|
|
384 |
To save and restore frame configuration, set this variable to |
|
385 |
'\(set-frame-configuration . current-frame-configuration\) |
|
386 |
|
|
387 |
NOTE: This may not work properly with own-frame minibuffer |
|
388 |
settings. Older versions saves/restores frame configuration, but |
|
389 |
the default has changed now to avoid flickering." |
|
390 |
:group 'helm |
|
391 |
:type 'sexp) |
|
392 |
|
|
393 |
(defcustom helm-display-function 'helm-default-display-buffer |
|
394 |
"Function used to display `helm-buffer'. |
|
395 |
|
|
396 |
Local value in `helm-buffer' will take precedence on this default |
|
397 |
value. Commands that are in `helm-commands-using-frame' will have |
|
398 |
`helm-buffer' displayed in frame, `helm-display-function' being |
|
399 |
ignored. |
|
400 |
If no local value found and current command is not one of |
|
401 |
`helm-commands-using-frame' use this default value. |
|
402 |
Function in charge of deciding which value use is |
|
403 |
`helm-resolve-display-function'. |
|
404 |
|
|
405 |
To set it locally to `helm-buffer' in helm sources use |
|
406 |
`helm-set-local-variable' in init function or use |
|
407 |
:display-function slot in `helm' call." |
|
408 |
:group 'helm |
|
409 |
:type 'symbol) |
|
410 |
|
|
411 |
(defcustom helm-case-fold-search 'smart |
|
412 |
"Adds 'smart' option to `case-fold-search'. |
|
413 |
Smart option ignores case for searches as long as there are no |
|
414 |
upper case characters in the pattern. |
|
415 |
|
|
416 |
Use nil or t to turn off smart behavior and use |
|
417 |
`case-fold-search' behavior. |
|
418 |
|
|
419 |
Default is smart. |
|
420 |
|
|
421 |
NOTE: Case fold search has no effect when searching asynchronous |
|
422 |
sources, which rely on customized features implemented directly |
|
423 |
into their execution process. See helm-grep.el for an example." |
|
424 |
:group 'helm |
|
425 |
:type '(choice (const :tag "Ignore case" t) |
|
426 |
(const :tag "Respect case" nil) |
|
427 |
(other :tag "Smart" 'smart))) |
|
428 |
|
|
429 |
(defcustom helm-file-name-case-fold-search |
|
430 |
(if (memq system-type |
|
431 |
'(cygwin windows-nt ms-dos darwin)) |
|
432 |
t |
|
433 |
helm-case-fold-search) |
|
434 |
"Local setting of `helm-case-fold-search' for reading filenames. |
|
435 |
|
|
436 |
See `helm-case-fold-search' for more info." |
|
437 |
:group 'helm |
|
438 |
:type 'symbol) |
|
439 |
|
|
440 |
(defcustom helm-reuse-last-window-split-state nil |
|
441 |
"Use the same state of window split, vertical or horizontal. |
|
442 |
`helm-toggle-resplit-window' for the next helm session will use |
|
443 |
the same window scheme as the previous session unless |
|
444 |
`helm-split-window-default-side' is 'same or 'other." |
|
445 |
:group 'helm |
|
446 |
:type 'boolean) |
|
447 |
|
|
448 |
(defcustom helm-split-window-preferred-function 'helm-split-window-default-fn |
|
449 |
"Default function used for splitting window." |
|
450 |
:group 'helm |
|
451 |
:type 'function) |
|
452 |
|
|
453 |
(defcustom helm-split-window-default-side 'below |
|
454 |
"The default side to display `helm-buffer'. |
|
455 |
Must be one acceptable arg for `split-window' SIDE, |
|
456 |
that is `below', `above', `left' or `right'. |
|
457 |
|
|
458 |
Other acceptable values are `same' which always display |
|
459 |
`helm-buffer' in current window and `other' that display |
|
460 |
`helm-buffer' below if only one window or in |
|
461 |
`other-window-for-scrolling' when available. |
|
462 |
|
|
463 |
A nil value has same effect as `below'. |
|
464 |
If `helm-full-frame' is non-`nil', it take precedence over this setting. |
|
465 |
|
|
466 |
See also `helm-split-window-inside-p' and `helm-always-two-windows' that |
|
467 |
take precedence over this. |
|
468 |
|
|
469 |
NOTE: this have no effect if `helm-split-window-preferred-function' is not |
|
470 |
`helm-split-window-default-fn' unless this new function can handle this." |
|
471 |
:group 'helm |
|
472 |
:type 'symbol) |
|
473 |
|
|
474 |
(defcustom helm-display-buffer-default-height nil |
|
475 |
"Initial height of `helm-buffer', specified as an integer or a function. |
|
476 |
|
|
477 |
The function should take one arg and the responsibility for |
|
478 |
re-sizing the window; function's return value is ignored. |
|
479 |
Note that this have no effect when the split is vertical. |
|
480 |
See `display-buffer' for more info." |
|
481 |
:group 'helm |
|
482 |
:type '(choice integer function)) |
|
483 |
|
|
484 |
(defcustom helm-display-buffer-default-width nil |
|
485 |
"Initial width of `helm-buffer', specified as an integer or a function. |
|
486 |
|
|
487 |
The function should take one arg and the responsibility for |
|
488 |
re-sizing the window; function's return value is ignored. |
|
489 |
Note that this have no effect when the split is horizontal. |
|
490 |
See `display-buffer' for more info." |
|
491 |
:group 'helm |
|
492 |
:type '(choice integer function)) |
|
493 |
|
|
494 |
(defvaralias 'helm-split-window-in-side-p 'helm-split-window-inside-p) |
|
495 |
(make-obsolete-variable 'helm-split-window-in-side-p 'helm-split-window-inside-p "2.8.6") |
|
496 |
(defcustom helm-split-window-inside-p nil |
|
497 |
"Forces split inside selected window when non-`nil'. |
|
498 |
See also `helm-split-window-default-side'. |
|
499 |
|
|
500 |
NOTE: this has no effect if |
|
501 |
`helm-split-window-preferred-function' is not |
|
502 |
`helm-split-window-default-fn' unless this new function can |
|
503 |
handle this." |
|
504 |
:group 'helm |
|
505 |
:type 'boolean) |
|
506 |
|
|
507 |
(defcustom helm-always-two-windows nil |
|
508 |
"When non-`nil' helm uses two windows in this frame. |
|
509 |
To display `helm-buffer' in one window and `helm-current-buffer' |
|
510 |
in the other. |
|
511 |
|
|
512 |
Note: this has no effect when `helm-split-window-inside-p' is non-`nil', |
|
513 |
or when `helm-split-window-default-side' is set to 'same. |
|
514 |
|
|
515 |
When `helm-autoresize-mode' is enabled, setting this to nil |
|
516 |
will have no effect. |
|
517 |
|
|
518 |
Also when non-`nil' it overrides the effect of `helm-split-window-default-side' |
|
519 |
set to `other'." |
|
520 |
:group 'helm |
|
521 |
:type 'boolean) |
|
522 |
|
|
523 |
(defcustom helm-display-buffer-width 72 |
|
524 |
"Frame width when displaying helm-buffer in own frame." |
|
525 |
:group 'helm |
|
526 |
:type 'integer) |
|
527 |
|
|
528 |
(defcustom helm-display-buffer-height 20 |
|
529 |
"Frame height when displaying helm-buffer in own frame." |
|
530 |
:group 'helm |
|
531 |
:type 'integer) |
|
532 |
|
|
533 |
(defcustom helm-default-display-buffer-functions nil |
|
534 |
"Action functions to pass to `display-buffer'. |
|
535 |
See (info \"(elisp) Display Action Functions\"). |
|
536 |
|
|
537 |
Have no effect when `helm-always-two-windows' is non-nil and may |
|
538 |
override other settings like `helm-split-window-inside-p'." |
|
539 |
:group 'helm |
|
540 |
:type '(repeat symbol)) |
|
541 |
|
|
542 |
(defcustom helm-default-display-buffer-alist nil |
|
543 |
"Additional alist to pass to `display-buffer' action. |
|
544 |
See (info \"(elisp) Display Action Functions\"). |
|
545 |
|
|
546 |
Have no effect when `helm-always-two-windows' is non-nil and may |
|
547 |
override other settings like `helm-split-window-inside-p'. |
|
548 |
Note that window-height and window-width have to be configured in |
|
549 |
`helm-display-buffer-height' and `helm-display-buffer-width'." |
|
550 |
:group 'helm |
|
551 |
:type '(alist :key-type symbol :value-type sexp)) |
|
552 |
|
|
553 |
(defcustom helm-sources-using-default-as-input '(helm-source-imenu |
|
554 |
helm-source-imenu-all |
|
555 |
helm-source-info-elisp |
|
556 |
helm-source-etags-select |
|
557 |
helm-source-man-pages |
|
558 |
helm-source-occur |
|
559 |
helm-source-moccur |
|
560 |
helm-source-grep-ag |
|
561 |
helm-source-grep-git |
|
562 |
helm-source-grep) |
|
563 |
"List of helm sources that need to use `helm--maybe-use-default-as-input'. |
|
564 |
When a source is a member of this list, default `thing-at-point' |
|
565 |
will be used as input." |
|
566 |
:group 'helm |
|
567 |
:type '(repeat (choice symbol))) |
|
568 |
|
|
569 |
(defcustom helm-delete-minibuffer-contents-from-point t |
|
570 |
"When non-`nil', `helm-delete-minibuffer-contents' delete region from `point'. |
|
571 |
Otherwise delete `minibuffer-contents'. |
|
572 |
See documentation for `helm-delete-minibuffer-contents'." |
|
573 |
:group 'helm |
|
574 |
:type 'boolean) |
|
575 |
|
|
576 |
(defcustom helm-follow-mode-persistent nil |
|
577 |
"When non-`nil', save last state of `helm-follow-mode' for the next emacs sessions. |
|
578 |
|
|
579 |
Each time you turn on or off `helm-follow-mode', the current source name will be stored |
|
580 |
or removed from `helm-source-names-using-follow'. |
|
581 |
|
|
582 |
Note that this may be disabled in some places where it is unsafe to use |
|
583 |
because persistent action is changing according to context." |
|
584 |
:group 'helm |
|
585 |
:type 'boolean) |
|
586 |
|
|
587 |
(defcustom helm-source-names-using-follow nil |
|
588 |
"A list of source names to have follow enabled. |
|
589 |
This list of source names will be used only |
|
590 |
when `helm-follow-mode-persistent' is non-nil. |
|
591 |
|
|
592 |
You don't have to customize this yourself unless you really want and |
|
593 |
know what you are doing, instead just set |
|
594 |
`helm-follow-mode-persistent' to non-nil and as soon you turn on or |
|
595 |
off `helm-follow-mode' (C-c C-f) in a source, helm will save or remove |
|
596 |
source name in this variable." |
|
597 |
:group 'helm |
|
598 |
:type '(repeat (choice string))) |
|
599 |
|
|
600 |
(defcustom helm-prevent-escaping-from-minibuffer t |
|
601 |
"Prevent escaping from minibuffer with `other-window' during the helm session." |
|
602 |
:group 'helm |
|
603 |
:type 'boolean) |
|
604 |
|
|
605 |
(defcustom helm-allow-mouse nil |
|
606 |
"Allow mouse usage during the helm session when non-nil. |
|
607 |
|
|
608 |
Note that this also allow moving out of minibuffer when clicking |
|
609 |
outside of `helm-buffer', up to you to get back to helm by clicking |
|
610 |
back in `helm-buffer' of minibuffer." |
|
611 |
:group 'helm |
|
612 |
:type 'boolean) |
|
613 |
|
|
614 |
(defcustom helm-move-to-line-cycle-in-source nil |
|
615 |
"Cycle to the beginning or end of the list after reaching the bottom or top. |
|
616 |
This applies when using `helm-next/previous-line'." |
|
617 |
:group 'helm |
|
618 |
:type 'boolean) |
|
619 |
|
|
620 |
(defcustom helm-fuzzy-match-fn 'helm-fuzzy-match |
|
621 |
"The function for fuzzy matching in `helm-source-sync' based sources." |
|
622 |
:group 'helm |
|
623 |
:type 'function) |
|
624 |
|
|
625 |
(defcustom helm-fuzzy-search-fn 'helm-fuzzy-search |
|
626 |
"The function for fuzzy matching in `helm-source-in-buffer' based sources." |
|
627 |
:group 'helm |
|
628 |
:type 'function) |
|
629 |
|
|
630 |
(defcustom helm-fuzzy-sort-fn 'helm-fuzzy-matching-default-sort-fn |
|
631 |
"The sort transformer function used in fuzzy matching. |
|
632 |
When nil, sorting is not done." |
|
633 |
:group 'helm |
|
634 |
:type 'function) |
|
635 |
|
|
636 |
(defcustom helm-fuzzy-matching-highlight-fn 'helm-fuzzy-default-highlight-match |
|
637 |
"The function to highlight fuzzy matches. |
|
638 |
When nil, no highlighting is done." |
|
639 |
:group 'helm |
|
640 |
:type 'function) |
|
641 |
|
|
642 |
(defcustom helm-autoresize-max-height 40 |
|
643 |
"Specifies maximum height and defaults to percent of helm window's frame height. |
|
644 |
|
|
645 |
See `fit-window-to-buffer' for more infos." |
|
646 |
:group 'helm |
|
647 |
:type 'integer) |
|
648 |
|
|
649 |
(defcustom helm-autoresize-min-height 10 |
|
650 |
"Specifies minimum height and defaults to percent of helm window's frame height. |
|
651 |
|
|
652 |
If nil, `window-min-height' is used. |
|
653 |
See `fit-window-to-buffer' for details." |
|
654 |
:group 'helm |
|
655 |
:type 'integer) |
|
656 |
|
|
657 |
(defcustom helm-input-method-verbose-flag nil |
|
658 |
"The default value for `input-method-verbose-flag' used in helm minibuffer. |
|
659 |
It is nil by default, which does not turn off input method. Helm |
|
660 |
updates and exits without interruption -- necessary for complex methods. |
|
661 |
|
|
662 |
If set to any other value as per `input-method-verbose-flag', |
|
663 |
then use `C-\\' to disable the `current-input-method' to exit or update helm" |
|
664 |
:group 'helm |
|
665 |
:type '(radio :tag "A flag to control extra guidance for input methods in helm." |
|
666 |
(const :tag "Never provide guidance" nil) |
|
667 |
(const :tag "Always provide guidance" t) |
|
668 |
(const :tag "Provide guidance only for complex methods" complex-only))) |
|
669 |
|
|
670 |
(defcustom helm-display-header-line t |
|
671 |
"Display header-line when non nil." |
|
672 |
:group 'helm |
|
673 |
:type 'boolean) |
|
674 |
|
|
675 |
(defcustom helm-inherit-input-method t |
|
676 |
"Inherit `current-input-method' from `current-buffer' when non-`nil'. |
|
677 |
The default is to enable this by default and then toggle |
|
678 |
`toggle-input-method'." |
|
679 |
:group 'helm |
|
680 |
:type 'boolean) |
|
681 |
|
|
682 |
(defcustom helm-echo-input-in-header-line nil |
|
683 |
"Send current input in header-line when non-nil." |
|
684 |
:group 'helm |
|
685 |
:type 'boolean) |
|
686 |
|
|
687 |
(defcustom helm-header-line-space-before-prompt 'left-fringe |
|
688 |
"Specify the space before prompt in header-line. |
|
689 |
|
|
690 |
This will be used when `helm-echo-input-in-header-line' is non-nil. |
|
691 |
|
|
692 |
Value can be one of the symbols 'left-fringe or 'left-margin or an |
|
693 |
integer specifying the number of spaces before prompt. |
|
694 |
Note that on input longer that `window-width' the continuation string |
|
695 |
will be shown on left side of window without taking care of this." |
|
696 |
:group 'helm |
|
697 |
:type '(choice |
|
698 |
(symbol |
|
699 |
(const :tag "Fringe" 'left-fringe) |
|
700 |
(const :tag "Margin" 'left-margin)) |
|
701 |
integer)) |
|
702 |
|
|
703 |
(defcustom helm-tramp-connection-min-time-diff 5 |
|
704 |
"Value of `tramp-connection-min-time-diff' for helm remote processes. |
|
705 |
If set to zero helm remote processes are not delayed. |
|
706 |
Setting this to a value less than 5 or disabling it with a zero value |
|
707 |
is risky, however on emacs versions starting at 24.5 it seems |
|
708 |
it is now possible to disable it. |
|
709 |
Anyway at any time in helm you can suspend your processes while typing |
|
710 |
by hitting \\<helm-map> `\\[helm-toggle-suspend-update]'. |
|
711 |
Only async sources than use a sentinel calling |
|
712 |
`helm-process-deferred-sentinel-hook' are affected by this." |
|
713 |
:type 'integer |
|
714 |
:group 'helm) |
|
715 |
|
|
716 |
(defcustom helm-debug-root-directory nil |
|
717 |
"When non-`nil', saves helm log messages to a file in this directory. |
|
718 |
When `nil' log messages are saved to a buffer instead. |
|
719 |
Log message are saved only when `helm-debug' is non-nil, so setting this |
|
720 |
doesn't enable debugging by itself. |
|
721 |
|
|
722 |
See `helm-log-save-maybe' for more info." |
|
723 |
:type 'string |
|
724 |
:group 'helm) |
|
725 |
|
|
726 |
(defcustom helm-show-action-window-other-window nil |
|
727 |
"Show action buffer beside `helm-buffer' when non-nil. |
|
728 |
|
|
729 |
If nil don't split and replace helm-buffer by the action buffer |
|
730 |
in same window. |
|
731 |
If left display the action buffer at the left of helm-buffer. |
|
732 |
If right or any other value, split at right. |
|
733 |
|
|
734 |
Note that this may not fit well with some helm window configurations, |
|
735 |
so it have only effect when `helm-always-two-windows' is non-nil." |
|
736 |
:group 'helm |
|
737 |
:type '(choice |
|
738 |
(const :tag "Split at left" left) |
|
739 |
(const :tag "Don't split" nil) |
|
740 |
(other :tag "Split at right" right))) |
|
741 |
|
|
742 |
(defcustom helm-cycle-resume-delay 1.0 |
|
743 |
"Delay used before resuming in `helm-run-cycle-resume'." |
|
744 |
:type 'float |
|
745 |
:group 'helm) |
|
746 |
|
|
747 |
(defcustom helm-display-buffer-reuse-frame nil |
|
748 |
"When non nil helm frame is not deleted and reused in next sessions. |
|
749 |
|
|
750 |
This was used to workaround a bug in emacs where frames where |
|
751 |
popping up slowly, now that the bug have been fixed upstream probably |
|
752 |
you don't want to use this anymore." |
|
753 |
:group 'helm |
|
754 |
:type 'boolean) |
|
755 |
|
|
756 |
(defcustom helm-commands-using-frame nil |
|
757 |
"A list of commands where `helm-buffer' is displayed in a frame." |
|
758 |
:group 'helm |
|
759 |
:type '(repeat symbol)) |
|
760 |
|
|
761 |
(defcustom helm-actions-inherit-frame-settings t |
|
762 |
"Actions inherit helm frame settings of initial command when non nil." |
|
763 |
:group 'helm |
|
764 |
:type 'boolean) |
|
765 |
|
|
766 |
(defcustom helm-use-undecorated-frame-option t |
|
767 |
"Display helm frame undecorated when non nil. |
|
768 |
|
|
769 |
This option have no effect with emacs versions lower than 26." |
|
770 |
:group 'helm |
|
771 |
:type 'boolean) |
|
772 |
|
|
773 |
(defcustom helm-use-frame-when-more-than-two-windows nil |
|
774 |
"Display helm buffer in frame when more than two windows." |
|
775 |
:group 'helm |
|
776 |
:type 'boolean) |
|
777 |
|
|
778 |
(defcustom helm-default-prompt-display-function |
|
779 |
#'helm-set-default-prompt-display |
|
780 |
"The function to use to set face of fake cursor in header-line." |
|
781 |
:group 'helm |
|
782 |
:type 'function) |
|
783 |
|
|
784 |
;;; Faces |
|
785 |
;; |
|
786 |
;; |
|
787 |
(defgroup helm-faces nil |
|
788 |
"Customize the appearance of helm." |
|
789 |
:prefix "helm-" |
|
790 |
:group 'faces |
|
791 |
:group 'helm) |
|
792 |
|
|
793 |
(defface helm-source-header |
|
794 |
'((((background dark)) |
|
795 |
:background "#22083397778B" |
|
796 |
:foreground "white" |
|
797 |
:weight bold :height 1.3 :family "Sans Serif") |
|
798 |
(((background light)) |
|
799 |
:background "#abd7f0" |
|
800 |
:foreground "black" |
|
801 |
:weight bold :height 1.3 :family "Sans Serif")) |
|
802 |
"Face for source header in the helm buffer." |
|
803 |
:group 'helm-faces) |
|
804 |
|
|
805 |
(defface helm-visible-mark |
|
806 |
'((((min-colors 88) (background dark)) |
|
807 |
(:background "green1" :foreground "black")) |
|
808 |
(((background dark)) |
|
809 |
(:background "green" :foreground "black")) |
|
810 |
(((background light)) :background "#d1f5ea") |
|
811 |
(((min-colors 88)) |
|
812 |
(:background "green1")) |
|
813 |
(t (:background "green"))) |
|
814 |
"Face for visible mark." |
|
815 |
:group 'helm-faces) |
|
816 |
|
|
817 |
(defface helm-header |
|
818 |
'((t (:inherit header-line))) |
|
819 |
"Face for header lines in the helm buffer." |
|
820 |
:group 'helm-faces) |
|
821 |
|
|
822 |
(defface helm-candidate-number |
|
823 |
'((((background dark)) :background "Yellow" :foreground "black") |
|
824 |
(((background light)) :background "#faffb5" :foreground "black")) |
|
825 |
"Face for candidate number in mode-line." |
|
826 |
:group 'helm-faces) |
|
827 |
|
|
828 |
(defface helm-candidate-number-suspended |
|
829 |
'((t (:inherit helm-candidate-number :inverse-video t))) |
|
830 |
"Face for candidate number in mode-line when helm is suspended." |
|
831 |
:group 'helm-faces) |
|
832 |
|
|
833 |
(defface helm-selection |
|
834 |
'((((background dark)) :background "ForestGreen" |
|
835 |
:distant-foreground "black") |
|
836 |
(((background light)) :background "#b5ffd1" |
|
837 |
:distant-foreground "black")) |
|
838 |
"Face for currently selected item in the helm buffer." |
|
839 |
:group 'helm-faces) |
|
840 |
|
|
841 |
(defface helm-separator |
|
842 |
'((((background dark)) :foreground "red") |
|
843 |
(((background light)) :foreground "#ffbfb5")) |
|
844 |
"Face for multiline source separator." |
|
845 |
:group 'helm-faces) |
|
846 |
|
|
847 |
(defface helm-action |
|
848 |
'((t (:underline t))) |
|
849 |
"Face for action lines in the helm action buffer." |
|
850 |
:group 'helm-faces) |
|
851 |
|
|
852 |
(defface helm-prefarg |
|
853 |
'((((background dark)) :foreground "green") |
|
854 |
(((background light)) :foreground "red")) |
|
855 |
"Face for showing prefix arg in mode-line." |
|
856 |
:group 'helm-faces) |
|
857 |
|
|
858 |
(defface helm-match |
|
859 |
'((((background light)) :foreground "#b00000") |
|
860 |
(((background dark)) :foreground "gold1")) |
|
861 |
"Face used to highlight matches." |
|
862 |
:group 'helm-faces) |
|
863 |
|
|
864 |
(defface helm-header-line-left-margin |
|
865 |
'((t (:foreground "black" :background "yellow"))) |
|
866 |
"Face used to highlight helm-header sign in left-margin." |
|
867 |
:group 'helm-faces) |
|
868 |
|
|
869 |
(defface helm-minibuffer-prompt |
|
870 |
'((t (:inherit minibuffer-prompt))) |
|
871 |
"Face used for the minibuffer/headline prompt (such as Pattern:) in helm." |
|
872 |
:group 'helm-faces) |
|
873 |
|
|
874 |
|
|
875 |
;;; Variables. |
|
876 |
;; |
|
877 |
;; |
|
878 |
(defvar helm-selection-overlay nil |
|
879 |
"Overlay used to highlight the currently selected item.") |
|
880 |
|
|
881 |
(defvar helm-async-processes nil |
|
882 |
"List of information about asynchronous processes managed by helm.") |
|
883 |
|
|
884 |
(defvar helm-before-initialize-hook nil |
|
885 |
"Runs before helm initialization. |
|
886 |
This hook runs before init functions in `helm-sources', which is |
|
887 |
before creation of `helm-buffer'. Set local variables for |
|
888 |
`helm-buffer' that need a value from `current-buffer' with |
|
889 |
`helm-set-local-variable'.") |
|
890 |
|
|
891 |
(defvar helm-after-initialize-hook nil |
|
892 |
"Runs after helm initialization. |
|
893 |
This hook runs after `helm-buffer' is created but not from |
|
894 |
`helm-buffer'. The hook needs to specify in which buffer to run.") |
|
895 |
|
|
896 |
(defvaralias 'helm-update-hook 'helm-after-update-hook) |
|
897 |
(make-obsolete-variable 'helm-update-hook 'helm-after-update-hook "1.9.9") |
|
898 |
|
|
899 |
(defvar helm-after-update-hook nil |
|
900 |
"Runs after updating the helm buffer with the new input pattern.") |
|
901 |
|
|
902 |
(defvar helm-cleanup-hook nil |
|
903 |
"Runs after exiting the minibuffer and before performing an |
|
904 |
action. |
|
905 |
|
|
906 |
This hook runs even if helm exits the minibuffer abnormally (e.g. |
|
907 |
via `helm-keyboard-quit').") |
|
908 |
|
|
909 |
(defvar helm-select-action-hook nil |
|
910 |
"Runs when opening the action buffer.") |
|
911 |
|
|
912 |
(defvar helm-before-action-hook nil |
|
913 |
"Runs before executing action. |
|
914 |
Unlike `helm-cleanup-hook', this hook runs before helm closes the |
|
915 |
minibuffer and also before performing an action.") |
|
916 |
|
|
917 |
(defvar helm-after-action-hook nil |
|
918 |
"Runs after executing action.") |
|
919 |
|
|
920 |
(defvar helm-exit-minibuffer-hook nil |
|
921 |
"Runs just before exiting the minibuffer. |
|
922 |
|
|
923 |
This hook runs when helm exits the minibuffer normally (e.g. via |
|
924 |
candidate selection), but does NOT run if helm exits the |
|
925 |
minibuffer abnormally (e.g. via `helm-keyboard-quit').") |
|
926 |
|
|
927 |
(defvar helm-after-persistent-action-hook nil |
|
928 |
"Runs after executing persistent action.") |
|
929 |
|
|
930 |
(defvar helm-move-selection-before-hook nil |
|
931 |
"Runs before moving selection in `helm-buffer'.") |
|
932 |
|
|
933 |
(defvar helm-move-selection-after-hook nil |
|
934 |
"Runs after moving selection in `helm-buffer'.") |
|
935 |
|
|
936 |
(defvar helm-after-preselection-hook nil |
|
937 |
"Runs after pre-selection in `helm-buffer'.") |
|
938 |
|
|
939 |
(defvar helm-window-configuration-hook nil |
|
940 |
"Runs when switching to and from the action buffer.") |
|
941 |
|
|
942 |
(defvar helm-execute-action-at-once-if-one nil |
|
943 |
"When non--nil executes the default action and then exits if only one candidate. |
|
944 |
If symbol 'current-source is given as value exit if only one candidate |
|
945 |
in current source. |
|
946 |
This variable accepts a function with no args that should returns a boolean |
|
947 |
value or 'current-source.") |
|
948 |
|
|
949 |
(defvar helm-quit-if-no-candidate nil |
|
950 |
"When non-`nil', quits if there are no candidates. |
|
951 |
This variable accepts a function.") |
|
952 |
|
|
953 |
(defvar helm-debug-variables nil |
|
954 |
"A list of helm variables that `helm-debug-output' displays. |
|
955 |
If `nil', `helm-debug-output' includes only variables with |
|
956 |
`helm-' prefixes.") |
|
957 |
|
|
958 |
(defvar helm-debug-buffer "*Debug Helm Log*") |
|
959 |
|
|
960 |
(defvar helm-debug nil |
|
961 |
"If non-`nil', write log message to `helm-debug-buffer'. |
|
962 |
Default is `nil', which disables writing log messages because the |
|
963 |
size of `helm-debug-buffer' grows quickly.") |
|
964 |
|
|
965 |
(defvar helm-mode-line-string "\ |
|
966 |
\\<helm-map>\ |
|
967 |
\\[helm-help]:Help \ |
|
968 |
\\[helm-select-action]:Act \ |
|
969 |
\\[helm-maybe-exit-minibuffer]/\ |
|
970 |
f1/f2/f-n:NthAct \ |
|
971 |
\\[helm-toggle-suspend-update]:Tog.suspend" |
|
972 |
"Help string displayed by helm in the mode-line. |
|
973 |
It is either a string or a list of two string arguments where the |
|
974 |
first string is the name and the second string is displayed in |
|
975 |
the mode-line. When `nil', uses default `mode-line-format'.") |
|
976 |
|
|
977 |
(defvar helm-minibuffer-set-up-hook nil |
|
978 |
"Hook that runs at minibuffer initialization. |
|
979 |
A hook useful for modifying minibuffer settings in helm. |
|
980 |
|
|
981 |
An example that hides the minibuffer when using |
|
982 |
`helm-echo-input-in-header-line': |
|
983 |
|
|
984 |
(add-hook 'helm-minibuffer-set-up-hook #'helm-hide-minibuffer-maybe) |
|
985 |
|
|
986 |
Note that we check `helm-echo-input-in-header-line' value |
|
987 |
from `helm-buffer' which allow detecting possible local |
|
988 |
value of this var.") |
|
989 |
|
|
990 |
(defvar helm-help-message |
|
991 |
"* Helm Generic Help |
|
992 |
** Basics |
|
993 |
|
|
994 |
Helm narrows down the list of candidates as you type a filter pattern. |
|
995 |
|
|
996 |
Helm accepts multiple space-separated patterns, each pattern can be negated with \"!\". |
|
997 |
|
|
998 |
Helm also supports fuzzy matching in some places when specified, you will find |
|
999 |
several variables to enable fuzzy matching in diverse sources, |
|
1000 |
see [[https://github.com/emacs-helm/helm/wiki/Fuzzy-matching][fuzzy-matching]] in helm-wiki for more infos. |
|
1001 |
|
|
1002 |
Helm generally uses familiar Emacs keys to navigate the list. |
|
1003 |
Here follow some of the less obvious bindings: |
|
1004 |
|
|
1005 |
- `\\[helm-maybe-exit-minibuffer]' selects the candidate from the list, executes the default action |
|
1006 |
upon exiting the Helm session. |
|
1007 |
|
|
1008 |
- `\\[helm-execute-persistent-action]' executes the default action but without exiting the Helm session. |
|
1009 |
Not all sources support this. |
|
1010 |
|
|
1011 |
- `\\[helm-select-action]' displays a list of actions available on current candidate or all marked candidates. |
|
1012 |
The default binding <tab> is ordinarily used for completion, but that would be |
|
1013 |
redundant since Helm completes upon every character entered in the prompt. |
|
1014 |
See [[https://github.com/emacs-helm/helm/wiki#helm-completion-vs-emacs-completion][Helm wiki]]. |
|
1015 |
|
|
1016 |
Note: In addition to the default actions list, additional actions appear |
|
1017 |
depending of the type of the selected candidate(s). They are called filtered |
|
1018 |
actions. |
|
1019 |
|
|
1020 |
** Helm mode |
|
1021 |
|
|
1022 |
`helm-mode' toggles Helm completion in native Emacs functions, |
|
1023 |
so when you turn `helm-mode' on, commands like `switch-to-buffer' will use |
|
1024 |
Helm completion instead of the usual Emacs completion buffer. |
|
1025 |
|
|
1026 |
*** What gets or does not get \"helmized\" when `helm-mode' is enabled? |
|
1027 |
|
|
1028 |
Helm provides generic completion on all Emacs functions using `completing-read', |
|
1029 |
`completion-in-region' and their derivatives, e.g. `read-file-name'. Helm |
|
1030 |
exposes a user variable to control which function to use for a specific Emacs |
|
1031 |
command: `helm-completing-read-handlers-alist'. If the function for a specific |
|
1032 |
command is nil, it turns off Helm completion. See the variable documentation |
|
1033 |
for more infos. |
|
1034 |
|
|
1035 |
*** Helm functions vs helmized Emacs functions |
|
1036 |
|
|
1037 |
While there are Helm functions that perform the same completion as other |
|
1038 |
helmized Emacs functions, e.g. `switch-to-buffer' and `helm-buffers-list', the |
|
1039 |
native Helm functions like `helm-buffers-list' can receive new features, the |
|
1040 |
allow marking candidates, they have several actions, etc. Whereas the helmized |
|
1041 |
Emacs functions only have Helm completion, one action and no more then Emacs can |
|
1042 |
provide for this function. This is the intended behavior. |
|
1043 |
|
|
1044 |
Generally you are better off using the native Helm command |
|
1045 |
than the helmized Emacs equivalent. |
|
1046 |
|
|
1047 |
** Helm help |
|
1048 |
|
|
1049 |
\\[helm-documentation]: Show all helm documentations concatenated in one org file. |
|
1050 |
|
|
1051 |
From a Helm session, just hit \\<helm-map>\\[helm-help] to have the |
|
1052 |
documentation for the current source followed by the global Helm documentation. |
|
1053 |
|
|
1054 |
While in the help buffer, most of the regular keybindings are available in an |
|
1055 |
Emacs buffers; the most important ones are shown in minibuffer. However due to |
|
1056 |
the implementation restrictions, no regular Emacs keymap is used (it runs in a |
|
1057 |
loop when reading the help buffer) they are hardcoded and not modifiable. |
|
1058 |
|
|
1059 |
The hard-coded documentation bindings are: |
|
1060 |
|
|
1061 |
| Key | Alternative keys | Command | |
|
1062 |
|-----------+------------------+---------------------| |
|
1063 |
| C-v | Space next | Scroll up | |
|
1064 |
| M-v | b prior | Scroll down | |
|
1065 |
| C-s | | Isearch forward | |
|
1066 |
| C-r | | Isearch backward | |
|
1067 |
| C-a | | Beginning of line | |
|
1068 |
| C-e | | End of line | |
|
1069 |
| C-f | right | Forward char | |
|
1070 |
| C-b | left | Backward char | |
|
1071 |
| C-n | down | Next line | |
|
1072 |
| C-p | up | Previous line | |
|
1073 |
| M-a | | Backward sentence | |
|
1074 |
| M-e | | Forward sentence | |
|
1075 |
| M-f | | Forward word | |
|
1076 |
| M-b | | Backward word | |
|
1077 |
| M-> | | End of buffer | |
|
1078 |
| M-< | | Beginning of buffer | |
|
1079 |
| C-<SPACE> | | Toggle mark | |
|
1080 |
| RET | | Follow org link | |
|
1081 |
| C-% | | Push org mark | |
|
1082 |
| C-& | | Goto org mark-ring | |
|
1083 |
| TAB | | Org cycle | |
|
1084 |
| M-<TAB> | | Toggle visibility | |
|
1085 |
| M-w | | Copy region | |
|
1086 |
| q | | Quit | |
|
1087 |
|
|
1088 |
** Customize Helm |
|
1089 |
|
|
1090 |
Helm provides a lot of user variables for extensive customization. |
|
1091 |
From any Helm session, type \\<helm-map>\\[helm-customize-group] to jump to the current source `custom' group. |
|
1092 |
Helm also has a special group for faces you can access via `M-x customize-group RET helm-faces'. |
|
1093 |
|
|
1094 |
Note: Some sources may not have their group set and default to the `helm' group. |
|
1095 |
|
|
1096 |
** Helm's basic operations and default key bindings |
|
1097 |
|
|
1098 |
| Key | Alternative Keys | Command | |
|
1099 |
|---------+------------------+----------------------------------------------------------------------| |
|
1100 |
| C-p | Up | Previous line | |
|
1101 |
| C-n | Down | Next line | |
|
1102 |
| M-v | prior | Previous page | |
|
1103 |
| C-v | next | Next page | |
|
1104 |
| Enter | | Execute first (default) action / Select | |
|
1105 |
| M-< | | First line | |
|
1106 |
| M-> | | Last line | |
|
1107 |
| C-M-S-v | M-prior, C-M-y | Previous page (other-window) | |
|
1108 |
| C-M-v | M-next | Next page (other-window) | |
|
1109 |
| Tab | C-i | Show action list | |
|
1110 |
| Left | | Previous source | |
|
1111 |
| Right | C-o | Next source | |
|
1112 |
| C-k | | Delete pattern (with prefix arg delete from point to end or all [1]) | |
|
1113 |
| C-j | C-z | Persistent action (Execute and keep Helm session) | |
|
1114 |
|
|
1115 |
\[1] Delete from point to end or all depending on the value of |
|
1116 |
`helm-delete-minibuffer-contents-from-point'. |
|
1117 |
|
|
1118 |
** Shortcuts for n-th action |
|
1119 |
|
|
1120 |
f1-f12: Execute n-th action where n is 1 to 12. |
|
1121 |
|
|
1122 |
** Shortcuts for executing the default action on the n-th candidate |
|
1123 |
|
|
1124 |
C-x <n>: Execute default action on the n-th candidate before currently selected candidate. |
|
1125 |
|
|
1126 |
C-c <n>: Execute default action on the n-th candidate after current selected candidate. |
|
1127 |
|
|
1128 |
\"n\" is limited to 1-9. For larger jumps use other navigation keys. Helm does |
|
1129 |
not display line numbers by default: enable them with the |
|
1130 |
\[[https://github.com/coldnew/linum-relative][linum-relative]] package and |
|
1131 |
`helm-linum-relative-mode'. |
|
1132 |
If you are using Emacs-26+ version you can use `global-display-line-numbers-mode' |
|
1133 |
which seems even better (don't forget to customize `display-line-numbers-type' to relative). |
|
1134 |
|
|
1135 |
** Mouse control in Helm |
|
1136 |
|
|
1137 |
A basic support for the mouse is provided when the user sets `helm-allow-mouse' to non-nil. |
|
1138 |
|
|
1139 |
- mouse-1 selects the candidate. |
|
1140 |
- mouse-2 executes the default action on selected candidate. |
|
1141 |
- mouse-3 pops up the action menu. |
|
1142 |
|
|
1143 |
Note: When mouse control is enabled in Helm, it also lets you click around and lose |
|
1144 |
the minibuffer focus: you'll have to click on the Helm buffer or the minibuffer |
|
1145 |
to retrieve control of your Helm session. |
|
1146 |
|
|
1147 |
** Marked candidates |
|
1148 |
|
|
1149 |
You can mark candidates to execute an action on all of them instead of the |
|
1150 |
current selected candidate only. (See bindings below.) Most Helm actions |
|
1151 |
operate on marked candidates unless candidate-marking is explicitely forbidden |
|
1152 |
for a specific source. |
|
1153 |
|
|
1154 |
- To mark/unmark a candidate, use \\[helm-toggle-visible-mark]. (See bindings below.) |
|
1155 |
With a numeric prefix arg mark ARG candidates forward, if ARG is negative |
|
1156 |
mark ARG candidates backward. |
|
1157 |
|
|
1158 |
- To mark all visible unmarked candidates at once in current source use \\[helm-mark-all]. |
|
1159 |
With a prefix argument, mark all candidates in all sources. |
|
1160 |
|
|
1161 |
- To unmark all visible marked candidates at once use \\[helm-unmark-all]. |
|
1162 |
|
|
1163 |
- To mark/unmark all candidates at once use \\[helm-toggle-all-marks]. |
|
1164 |
With a prefix argument, mark/unmark all candidates in all sources. |
|
1165 |
|
|
1166 |
Note: When multiple candidates are selected across different sources, only the |
|
1167 |
candidates of the current source will be used when executing most actions (as |
|
1168 |
different sources can have different actions). Some actions support |
|
1169 |
multi-source marking however. |
|
1170 |
|
|
1171 |
** Follow candidates |
|
1172 |
|
|
1173 |
When `helm-follow-mode' is on (\\<helm-map>\\[helm-follow-mode] to toggle it), |
|
1174 |
moving up and down the Helm session or updating the list of candidates will |
|
1175 |
automatically execute the persistent-action as specified for the current source. |
|
1176 |
|
|
1177 |
If `helm-follow-mode-persistent' is non-nil, the state of the mode will be |
|
1178 |
restored for the following Helm sessions. |
|
1179 |
|
|
1180 |
If you just want to follow candidates occasionally without enabling |
|
1181 |
`helm-follow-mode', you can use \\<helm-map>\\[helm-follow-action-forward] or \\[helm-follow-action-backward] instead. |
|
1182 |
Conversely, when `helm-follow-mode' is enabled, those commands |
|
1183 |
go to previous/next line without executing the persistent action. |
|
1184 |
|
|
1185 |
** Frequently Used Commands |
|
1186 |
|
|
1187 |
\\[helm-toggle-resplit-and-swap-windows]\t\tToggle vertical/horizontal split on first hit and swap Helm window on second hit. |
|
1188 |
\\[helm-quit-and-find-file]\t\tDrop into `helm-find-files'. |
|
1189 |
\\[helm-kill-selection-and-quit]\t\tKill display value of candidate and quit (with prefix arg, kill the real value). |
|
1190 |
\\[helm-yank-selection]\t\tYank current selection into pattern. |
|
1191 |
\\[helm-copy-to-buffer]\t\tCopy selected candidate at point in current buffer. |
|
1192 |
\\[helm-follow-mode]\t\tToggle automatic execution of persistent action. |
|
1193 |
\\[helm-follow-action-forward]\tRun persistent action then select next line. |
|
1194 |
\\[helm-follow-action-backward]\t\tRun persistent action then select previous line. |
|
1195 |
\\[helm-refresh]\t\tRecalculate and redisplay candidates. |
|
1196 |
\\[helm-toggle-suspend-update]\t\tToggle candidate updates. |
|
1197 |
|
|
1198 |
** Special yes, no or yes for all answers |
|
1199 |
|
|
1200 |
You may be prompted in the minibuffer to answer by [y,n,!,q] in some places |
|
1201 |
for confirmation. |
|
1202 |
|
|
1203 |
- y mean yes |
|
1204 |
- no mean no |
|
1205 |
- ! mean yes for all |
|
1206 |
- q mean quit or abort current operation. |
|
1207 |
|
|
1208 |
When using ! you will not be prompted anymore for the same thing in current operation |
|
1209 |
e.g. file deletion, file copy etc... |
|
1210 |
|
|
1211 |
** Moving in `helm-buffer' |
|
1212 |
|
|
1213 |
You can move in `helm-buffer' with the usual commands used in Emacs: |
|
1214 |
\(\\<helm-map>\\[helm-next-line], \\<helm-map>\\[helm-previous-line], etc. See above basic commands. |
|
1215 |
When `helm-buffer' contains more than one source, change source with \\<helm-map>\\[helm-next-source]. |
|
1216 |
|
|
1217 |
Note: When reaching the end of a source, \\<helm-map>\\[helm-next-line] will *not* go to the next source unless |
|
1218 |
variable `helm-move-to-line-cycle-in-source' is non-nil, so you will have to use \\<helm-map>\\[helm-next-source]. |
|
1219 |
|
|
1220 |
** Resume previous session from current Helm session |
|
1221 |
|
|
1222 |
You can use `C-c n' (`helm-run-cycle-resume') to cycle in resumables sources. |
|
1223 |
`C-c n' is a special key set with `helm-define-key-with-subkeys' which, after pressing it, allows you |
|
1224 |
to keep cycling with further `n'. |
|
1225 |
|
|
1226 |
Tip: You can bound the same key in `global-map' to `helm-cycle-resume' |
|
1227 |
with `helm-define-key-with-subkeys' to let you transparently cycle |
|
1228 |
sessions, Helm fired up or not. |
|
1229 |
You can also bind the cycling commands to single key presses (e.g. `S-<f1>') this time |
|
1230 |
with a simple `define-key'. (Note that `S-<f1>' is not available in terminals.) |
|
1231 |
|
|
1232 |
Note: `helm-define-key-with-subkeys' is available only once Helm is loaded. |
|
1233 |
|
|
1234 |
You can also use \\<helm-map>\\[helm-resume-previous-session-after-quit] to resume |
|
1235 |
the previous session, or \\<helm-map>\\[helm-resume-list-buffers-after-quit] |
|
1236 |
to have completion on all resumable buffers. |
|
1237 |
|
|
1238 |
** Global commands |
|
1239 |
|
|
1240 |
*** Resume Helm session from outside Helm |
|
1241 |
|
|
1242 |
\\<global-map>\\[helm-resume] revives the last `helm' session. Binding a key to |
|
1243 |
this command will greatly improve `helm' interactivity, e.g. when quitting Helm |
|
1244 |
accidentally. |
|
1245 |
|
|
1246 |
You can call \\<global-map>\\[helm-resume] with a prefix argument to choose |
|
1247 |
\(with completion!) which session you'd like to resume. You can also cycle in |
|
1248 |
these sources with `helm-cycle-resume' (see above). |
|
1249 |
|
|
1250 |
** Debugging Helm |
|
1251 |
|
|
1252 |
Helm exposes the special variable `helm-debug': setting it to non-nil |
|
1253 |
will enable Helm logging in a special outline-mode buffer. |
|
1254 |
Helm resets the variable to nil at the end of each session. |
|
1255 |
|
|
1256 |
For convenience, \\<helm-map>\\[helm-enable-or-switch-to-debug] |
|
1257 |
allows you to turn on debugging for this session only. |
|
1258 |
To avoid accumulating log entries while you are typing patterns, you can use |
|
1259 |
\\<helm-map>\\[helm-toggle-suspend-update] to turn off updating. When you |
|
1260 |
are ready turn it on again to resume logging. |
|
1261 |
|
|
1262 |
Once you exit your Helm session you can access the debug buffer with |
|
1263 |
`helm-debug-open-last-log'. It is possible to save logs to dated files when |
|
1264 |
`helm-debug-root-directory' is set to a valid directory. |
|
1265 |
|
|
1266 |
Note: Be aware that Helm log buffers grow really fast, so use `helm-debug' only |
|
1267 |
when needed. |
|
1268 |
|
|
1269 |
** Writing your own Helm sources |
|
1270 |
|
|
1271 |
Writing simple sources for your own usage is easy. When calling the `helm' |
|
1272 |
function, the sources are added the :sources slot which can be a symbol or a |
|
1273 |
list of sources. Sources can be built with different EIEIO classes depending |
|
1274 |
what you want to do. To simplify this, several `helm-build-*' macros are |
|
1275 |
provided. Below, simple examples to start with. |
|
1276 |
|
|
1277 |
We will not go further here, see [[https://github.com/emacs-helm/helm/wiki/Developing][Helm wiki]] and the source |
|
1278 |
code for more information and more complex exapmles. |
|
1279 |
|
|
1280 |
#+begin_src elisp |
|
1281 |
|
|
1282 |
;; Candidates are stored in a list. |
|
1283 |
(helm :sources (helm-build-sync-source \"test\" |
|
1284 |
;; A function can be used as well |
|
1285 |
;; to provide candidates. |
|
1286 |
:candidates '(\"foo\" \"bar\" \"baz\")) |
|
1287 |
:buffer \"*helm test*\") |
|
1288 |
|
|
1289 |
;; Candidates are stored in a buffer. |
|
1290 |
;; Generally faster but doesn't allow a dynamic updating |
|
1291 |
;; of the candidates list i.e the list is fixed on start. |
|
1292 |
(helm :sources (helm-build-in-buffer-source \"test\" |
|
1293 |
:data '(\"foo\" \"bar\" \"baz\")) |
|
1294 |
:buffer \"*helm test*\") |
|
1295 |
|
|
1296 |
#+end_src |
|
1297 |
|
|
1298 |
** Helm Map |
|
1299 |
\\{helm-map}" |
|
1300 |
"Message string containing detailed help for `helm'. |
|
1301 |
It also accepts function or variable symbol.") |
|
1302 |
|
|
1303 |
(defvar helm-autoresize-mode) ;; Undefined in `helm-default-display-buffer'. |
|
1304 |
|
|
1305 |
(defvar helm-async-outer-limit-hook nil |
|
1306 |
"A hook that run in async sources when process output comes out of `candidate-number-limit'. |
|
1307 |
Should be set locally to `helm-buffer' with `helm-set-local-variable'.") |
|
1308 |
|
|
1309 |
(defvar helm-quit-hook nil |
|
1310 |
"A hook that run when quitting helm.") |
|
1311 |
|
|
1312 |
(defvar helm-resume-after-hook nil |
|
1313 |
"A hook that run after resuming a helm session. |
|
1314 |
The hook should take one arg SOURCES.") |
|
1315 |
|
|
1316 |
;;; Internal Variables |
|
1317 |
;; |
|
1318 |
;; |
|
1319 |
(defvar helm-source-filter nil |
|
1320 |
"A list of source names to be displayed. |
|
1321 |
Other sources won't appear in the search results. |
|
1322 |
If nil, no filtering is done. |
|
1323 |
Don't set this directly, use `helm-set-source-filter' during helm session |
|
1324 |
to modify it.") |
|
1325 |
(defvar helm-current-prefix-arg nil |
|
1326 |
"Record `current-prefix-arg' when exiting minibuffer.") |
|
1327 |
(defvar helm-saved-action nil |
|
1328 |
"Saved value of the currently selected action by key.") |
|
1329 |
(defvar helm-saved-current-source nil |
|
1330 |
"Value of the current source when the action list is shown.") |
|
1331 |
(defvar helm-in-persistent-action nil |
|
1332 |
"Flag whether in persistent-action or not.") |
|
1333 |
(defvar helm-last-buffer nil |
|
1334 |
"`helm-buffer' of previously `helm' session.") |
|
1335 |
(defvar helm-saved-selection nil |
|
1336 |
"Value of the currently selected object when the action list is shown.") |
|
1337 |
(defvar helm-sources nil |
|
1338 |
"[INTERNAL] Value of current sources in use, a list.") |
|
1339 |
(defvar helm-buffer-file-name nil |
|
1340 |
"Variable `buffer-file-name' when `helm' is invoked.") |
|
1341 |
(defvar helm-candidate-cache (make-hash-table :test 'equal) |
|
1342 |
"Holds the available candidate within a single helm invocation.") |
|
1343 |
(defvar helm--candidate-buffer-alist nil) |
|
1344 |
(defvar helm-input "" |
|
1345 |
"The input typed in the candidates panel.") |
|
1346 |
(defvar helm-input-local nil |
|
1347 |
"Internal, store locally `helm-pattern' value for later use in `helm-resume'.") |
|
1348 |
(defvar helm--source-name nil) |
|
1349 |
(defvar helm-current-source nil) |
|
1350 |
(defvar helm-tick-hash (make-hash-table :test 'equal)) |
|
1351 |
(defvar helm-issued-errors nil) |
|
1352 |
(defvar helm--last-log-file nil |
|
1353 |
"The name of the log file of the last helm session.") |
|
1354 |
(defvar helm--local-variables nil) |
|
1355 |
(defvar helm-split-window-state nil) |
|
1356 |
(defvar helm--window-side-state nil) |
|
1357 |
(defvar helm-selection-point nil) |
|
1358 |
(defvar helm-alive-p nil) |
|
1359 |
(defvar helm-visible-mark-overlays nil) |
|
1360 |
(defvar helm-update-blacklist-regexps '("^" "^ *" "$" "!" " " "\\b" |
|
1361 |
"\\<" "\\>" "\\_<" "\\_>" ".*" |
|
1362 |
"??" "?*" "*?" "?")) |
|
1363 |
(defvar helm--force-updating-p nil |
|
1364 |
"[INTERNAL] Don't use this in your programs.") |
|
1365 |
(defvar helm-exit-status 0 |
|
1366 |
"Flag to inform if helm did exit or quit. |
|
1367 |
0 means helm did exit when executing an action. |
|
1368 |
1 means helm did quit with \\[keyboard-quit] |
|
1369 |
Knowing this exit-status could help restore a window config when helm aborts |
|
1370 |
in some special circumstances. |
|
1371 |
See `helm-exit-minibuffer' and `helm-keyboard-quit'.") |
|
1372 |
(defvar helm-minibuffer-confirm-state nil) |
|
1373 |
(defvar helm-quit nil) |
|
1374 |
(defvar helm-buffers nil |
|
1375 |
"Helm buffers listed in order of most recently used.") |
|
1376 |
(defvar helm-current-position nil |
|
1377 |
"Cons of \(point . window-start\) when `helm' is invoked. |
|
1378 |
`helm-current-buffer' uses this to restore position after |
|
1379 |
`helm-keyboard-quit'") |
|
1380 |
(defvar helm-last-frame-or-window-configuration nil |
|
1381 |
"Used to store window or frame configuration at helm start.") |
|
1382 |
(defvar helm-onewindow-p nil) |
|
1383 |
(defvar helm-types nil) |
|
1384 |
(defvar helm--mode-line-string-real nil) ; The string to display in mode-line. |
|
1385 |
(defvar helm-persistent-action-display-window nil) |
|
1386 |
(defvar helm-marked-candidates nil |
|
1387 |
"Marked candidates. List of \(source . real\) pair.") |
|
1388 |
(defvar helm--mode-line-display-prefarg nil) |
|
1389 |
(defvar helm--temp-follow-flag nil |
|
1390 |
"[INTERNAL] A simple flag to notify persistent action we are following.") |
|
1391 |
(defvar helm--reading-passwd-or-string nil) |
|
1392 |
(defvar helm--in-update nil) |
|
1393 |
(defvar helm--in-fuzzy nil) |
|
1394 |
(defvar helm--maybe-use-default-as-input nil |
|
1395 |
"Flag to notify the use of use-default-as-input. |
|
1396 |
Use only in let-bindings. |
|
1397 |
Use :default arg of `helm' as input to update display. |
|
1398 |
Note that if also :input is specified as `helm' arg, it will take |
|
1399 |
precedence on :default.") |
|
1400 |
(defvar helm--temp-hooks nil |
|
1401 |
"Store temporary hooks added by `with-helm-temp-hook'.") |
|
1402 |
(defvar helm-truncate-lines nil |
|
1403 |
"[Internal] Don't set this globally, it is used as a local var.") |
|
1404 |
(defvar helm--prompt nil) |
|
1405 |
(defvar helm--file-completion-sources |
|
1406 |
'("Find Files" "Read File Name") |
|
1407 |
"Sources that use the *find-files mechanism can be added here. |
|
1408 |
Sources generated by `helm-mode' don't need to be added here |
|
1409 |
because they are automatically added. |
|
1410 |
|
|
1411 |
You should not modify this yourself unless you know what you are doing.") |
|
1412 |
(defvar helm--completing-file-name nil |
|
1413 |
"Non nil when `helm-read-file-name' is running. |
|
1414 |
Used for `helm-file-completion-source-p'.") |
|
1415 |
;; Same as `ffap-url-regexp' but keep it here to ensure `ffap-url-regexp' is not nil. |
|
1416 |
(defvar helm--url-regexp "\\`\\(news\\(post\\)?:\\|mailto:\\|file:\\|\\(ftp\\|https?\\|telnet\\|gopher\\|www\\|wais\\)://\\)") |
|
1417 |
(defvar helm--ignore-errors nil |
|
1418 |
"Flag to prevent helm popping up errors in candidates functions. |
|
1419 |
Should be set in candidates functions if needed, will be restored |
|
1420 |
at end of session.") |
|
1421 |
(defvar helm--action-prompt "Select action: ") |
|
1422 |
(defvar helm--cycle-resume-iterator nil) |
|
1423 |
(defvar helm--buffer-in-new-frame-p nil) |
|
1424 |
(defvar helm-initial-frame nil |
|
1425 |
"[INTERNAL] The selected frame before starting helm. |
|
1426 |
Helm use this internally to know in which frame it started, don't |
|
1427 |
modify this yourself.") |
|
1428 |
(defvar helm-popup-frame nil |
|
1429 |
"The frame where helm is displayed. |
|
1430 |
|
|
1431 |
This is only used when helm is using |
|
1432 |
`helm-display-buffer-in-own-frame' as `helm-display-function' and |
|
1433 |
`helm-display-buffer-reuse-frame' is non nil.") |
|
1434 |
(defvar helm--nested nil) |
|
1435 |
(defconst helm--frame-default-attributes |
|
1436 |
'(width height tool-bar-lines left top |
|
1437 |
title undecorated vertical-scroll-bars |
|
1438 |
visibility fullscreen menu-bar-lines undecorated) |
|
1439 |
"Frame parameters to save in `helm--last-frame-parameters'.") |
|
1440 |
(defvar helm--last-frame-parameters nil |
|
1441 |
"Frame parameters to save for later resuming. |
|
1442 |
Local to `helm-buffer'.") |
|
1443 |
(defvar helm--executing-helm-action nil |
|
1444 |
"Non nil when action is triggering a new helm-session. |
|
1445 |
This may be let bounded in other places to notify the display function |
|
1446 |
to reuse the same frame parameters as the previous helm session just |
|
1447 |
like resume would do.") |
|
1448 |
(defvar helm--current-buffer-narrowed nil) |
|
1449 |
(defvar helm--suspend-update-interactive-flag nil) |
|
1450 |
|
|
1451 |
;; Utility: logging |
|
1452 |
(defun helm-log (format-string &rest args) |
|
1453 |
"Log message `helm-debug' is non-`nil'. |
|
1454 |
Messages are written to the `helm-debug-buffer' buffer. |
|
1455 |
|
|
1456 |
Argument FORMAT-STRING is a string to use with `format'. |
|
1457 |
Use optional arguments ARGS like in `format'." |
|
1458 |
(when helm-debug |
|
1459 |
(with-current-buffer (get-buffer-create helm-debug-buffer) |
|
1460 |
(outline-mode) |
|
1461 |
(buffer-disable-undo) |
|
1462 |
(let ((inhibit-read-only t)) |
|
1463 |
(goto-char (point-max)) |
|
1464 |
(insert (let ((tm (current-time))) |
|
1465 |
(format (concat (if (string-match "Start session" format-string) |
|
1466 |
"* " "** ") |
|
1467 |
"%s.%06d (%s)\n %s\n") |
|
1468 |
(format-time-string "%H:%M:%S" tm) |
|
1469 |
(nth 2 tm) |
|
1470 |
(helm-log-get-current-function) |
|
1471 |
(apply #'format (cons format-string args))))))))) |
|
1472 |
|
|
1473 |
(defun helm-log-run-hook (hook) |
|
1474 |
"Run HOOK like `run-hooks' but write these actions to helm log buffer." |
|
1475 |
(helm-log "Executing %s with value = %S" hook (symbol-value hook)) |
|
1476 |
(helm-log "Executing %s with global value = %S" hook (default-value hook)) |
|
1477 |
(run-hooks hook) |
|
1478 |
(helm-log "executed %s" hook)) |
|
1479 |
|
|
1480 |
(defun helm-log-get-current-function () |
|
1481 |
"Get name of function that is calling `helm-log'. |
|
1482 |
The original idea is from `tramp-debug-message'." |
|
1483 |
(cl-loop with exclude-func-re = "^helm-\\(?:interpret\\|log\\|.*funcall\\)" |
|
1484 |
for btn from 1 to 40 |
|
1485 |
for btf = (cl-second (backtrace-frame btn)) |
|
1486 |
for fn = (if (symbolp btf) (symbol-name btf) "") |
|
1487 |
if (and (string-match "^helm" fn) |
|
1488 |
(not (string-match exclude-func-re fn))) |
|
1489 |
return fn)) |
|
1490 |
|
|
1491 |
(defun helm-log-error (&rest args) |
|
1492 |
"Accumulate error messages into `helm-issued-errors'. |
|
1493 |
ARGS are args given to `format'. |
|
1494 |
e.g (helm-log-error \"Error %s: %s\" (car err) (cdr err))." |
|
1495 |
(apply 'helm-log (concat "ERROR: " (car args)) (cdr args)) |
|
1496 |
(let ((msg (apply 'format args))) |
|
1497 |
(unless (member msg helm-issued-errors) |
|
1498 |
(cl-pushnew msg helm-issued-errors :test 'equal)))) |
|
1499 |
|
|
1500 |
(defun helm-log-save-maybe () |
|
1501 |
"Save log buffer when `helm-debug-root-directory' is non nil. |
|
1502 |
Create `helm-debug-root-directory' directory if necessary. |
|
1503 |
Messages are logged to a file named with todays date and time in this directory." |
|
1504 |
(when (and (stringp helm-debug-root-directory) |
|
1505 |
(not (file-directory-p helm-debug-root-directory))) |
|
1506 |
(make-directory helm-debug-root-directory t)) |
|
1507 |
(when helm-debug |
|
1508 |
(let ((logdir (expand-file-name (concat "helm-debug-" |
|
1509 |
(format-time-string "%Y%m%d")) |
|
1510 |
helm-debug-root-directory))) |
|
1511 |
(make-directory logdir t) |
|
1512 |
(with-current-buffer (get-buffer-create helm-debug-buffer) |
|
1513 |
(write-region (point-min) (point-max) |
|
1514 |
(setq helm--last-log-file |
|
1515 |
(expand-file-name |
|
1516 |
(format-time-string "%Y%m%d-%H%M%S") |
|
1517 |
logdir)) |
|
1518 |
nil 'silent) |
|
1519 |
(kill-buffer)))) |
|
1520 |
(setq helm-debug nil)) |
|
1521 |
|
|
1522 |
;;;###autoload |
|
1523 |
(defun helm-debug-open-last-log () |
|
1524 |
"Open helm log file or buffer of last helm session." |
|
1525 |
(interactive) |
|
1526 |
(if helm--last-log-file |
|
1527 |
(progn |
|
1528 |
(find-file helm--last-log-file) |
|
1529 |
(outline-mode) (view-mode 1) (visual-line-mode 1)) |
|
1530 |
(switch-to-buffer helm-debug-buffer) |
|
1531 |
(view-mode 1) (visual-line-mode 1))) |
|
1532 |
|
|
1533 |
(defun helm-print-error-messages () |
|
1534 |
"Print error messages in `helm-issued-errors'." |
|
1535 |
(and helm-issued-errors |
|
1536 |
(message "Helm issued errors: %s" |
|
1537 |
(mapconcat 'identity (reverse helm-issued-errors) "\n")))) |
|
1538 |
|
|
1539 |
|
|
1540 |
;; Test tools |
|
1541 |
(defmacro with-helm-time-after-update (&rest body) |
|
1542 |
(helm-with-gensyms (start-time time-elapsed) |
|
1543 |
`(let ((,start-time (float-time)) ,time-elapsed) |
|
1544 |
(add-hook 'helm-after-update-hook |
|
1545 |
(lambda () |
|
1546 |
(setq ,time-elapsed (- (float-time) ,start-time)) |
|
1547 |
(keyboard-quit))) |
|
1548 |
(unwind-protect ,@body |
|
1549 |
(remove-hook 'helm-after-update-hook |
|
1550 |
(lambda () |
|
1551 |
(setq ,time-elapsed (- (float-time) ,start-time)) |
|
1552 |
(keyboard-quit)))) |
|
1553 |
,time-elapsed))) |
|
1554 |
|
|
1555 |
|
|
1556 |
;; Helm API |
|
1557 |
(defmacro with-helm-default-directory (directory &rest body) |
|
1558 |
(declare (indent 2) (debug t)) |
|
1559 |
`(let ((default-directory (or (and ,directory |
|
1560 |
(file-name-as-directory ,directory)) |
|
1561 |
default-directory))) |
|
1562 |
,@body)) |
|
1563 |
|
|
1564 |
(defun helm-default-directory () |
|
1565 |
"Return the local value of `default-directory' in `helm-buffer'." |
|
1566 |
(buffer-local-value 'default-directory (get-buffer helm-buffer))) |
|
1567 |
|
|
1568 |
(defmacro with-helm-temp-hook (hook &rest body) |
|
1569 |
"Execute temporarily BODY as a function for HOOK." |
|
1570 |
(declare (indent 1) (debug t)) |
|
1571 |
(helm-with-gensyms (helm--hook) |
|
1572 |
`(progn |
|
1573 |
(defun ,helm--hook () |
|
1574 |
(unwind-protect |
|
1575 |
(progn ,@body) |
|
1576 |
(remove-hook ,hook (quote ,helm--hook)) |
|
1577 |
(fmakunbound (quote ,helm--hook)))) |
|
1578 |
(push (cons ',helm--hook ,hook) helm--temp-hooks) |
|
1579 |
(add-hook ,hook (quote ,helm--hook))))) |
|
1580 |
|
|
1581 |
(defmacro with-helm-after-update-hook (&rest body) |
|
1582 |
"Execute BODY at end of `helm-update'." |
|
1583 |
(declare (indent 0) (debug t)) |
|
1584 |
`(with-helm-temp-hook 'helm-after-update-hook ,@body)) |
|
1585 |
|
|
1586 |
(defmacro with-helm-alive-p (&rest body) |
|
1587 |
"Return error when BODY run outside helm context." |
|
1588 |
(declare (indent 0) (debug t)) |
|
1589 |
`(progn |
|
1590 |
(if helm-alive-p |
|
1591 |
(progn ,@body) |
|
1592 |
(error "Running helm command outside of context")))) |
|
1593 |
|
|
1594 |
(defmacro with-helm-in-frame (&rest body) |
|
1595 |
"Execute helm function in BODY displaying `helm-buffer' in separate frame." |
|
1596 |
(declare (debug t) (indent 0)) |
|
1597 |
`(progn |
|
1598 |
(helm-set-local-variable |
|
1599 |
'helm-display-function 'helm-display-buffer-in-own-frame) |
|
1600 |
,@body)) |
|
1601 |
|
|
1602 |
;;; helm-attributes |
|
1603 |
;; |
|
1604 |
(defun helm-attr (attribute-name &optional source compute) |
|
1605 |
"Get the value of ATTRIBUTE-NAME of SRC. |
|
1606 |
|
|
1607 |
If SRC is omitted, use current source. |
|
1608 |
|
|
1609 |
If COMPUTE is non-`nil' compute value of ATTRIBUTE-NAME with |
|
1610 |
`helm-interpret-value'. COMPUTE can have also 'ignorefn as value, in |
|
1611 |
this case `helm-interpret-value' will return a function as value |
|
1612 |
unchanged, but will eval a symbol which is bound. |
|
1613 |
|
|
1614 |
You can use `setf' to modify value of ATTRIBUTE-NAME unless COMPUTE is |
|
1615 |
specified, if attribute ATTRIBUTE-NAME is not found in SOURCE `setf' |
|
1616 |
will create new attribute ATTRIBUTE-NAME with specified value. |
|
1617 |
You can also use `helm-attrset' to modify ATTRIBUTE-NAME." |
|
1618 |
(declare (gv-setter |
|
1619 |
(lambda (val) |
|
1620 |
`(let* ((src (or ,source (helm-get-current-source))) |
|
1621 |
(attr (assq ,attribute-name src))) |
|
1622 |
(cl-assert (null ,compute) nil |
|
1623 |
"`setf' can't set the computed value of attribute `%s'" |
|
1624 |
,attribute-name) |
|
1625 |
(if attr |
|
1626 |
(setcdr attr ,val) |
|
1627 |
(and (setcdr src (cons (cons ,attribute-name ,val) |
|
1628 |
(cdr src))) |
|
1629 |
,val)))))) |
|
1630 |
(let ((src (or source (helm-get-current-source)))) |
|
1631 |
(helm-aif (assq attribute-name src) |
|
1632 |
(if compute |
|
1633 |
(helm-interpret-value (cdr it) src compute) |
|
1634 |
(cdr it))))) |
|
1635 |
|
|
1636 |
(cl-defun helm-attrset (attribute-name value |
|
1637 |
&optional |
|
1638 |
(src (helm-get-current-source))) |
|
1639 |
"Set the value of ATTRIBUTE-NAME of source SRC to VALUE. |
|
1640 |
|
|
1641 |
If ATTRIBUTE-NAME doesn't exists in source it is created with value VALUE.. |
|
1642 |
If SRC is omitted, use current source. |
|
1643 |
If operation succeed, return value, otherwise nil. |
|
1644 |
|
|
1645 |
Note that `setf' on `helm-attr' can be used instead of this function." |
|
1646 |
(setf (helm-attr attribute-name src) value)) |
|
1647 |
|
|
1648 |
(defun helm-add-action-to-source (name fn source &optional index) |
|
1649 |
"Add new action NAME linked to function FN to SOURCE. |
|
1650 |
Function FN should be a valid function that takes one arg i.e candidate, |
|
1651 |
argument NAME is a string that will appear in action menu |
|
1652 |
and SOURCE should be an existing helm source already loaded. |
|
1653 |
If INDEX is specified, action is added to the action list at INDEX, |
|
1654 |
otherwise added at end. |
|
1655 |
This allows users to add specific actions to an existing source |
|
1656 |
without modifying source code." |
|
1657 |
(let ((actions (helm-attr 'action source 'ignorefn)) |
|
1658 |
(new-action (list (cons name fn)))) |
|
1659 |
(when (functionp actions) |
|
1660 |
(setq actions (list (cons "Default action" actions)))) |
|
1661 |
(helm-attrset 'action |
|
1662 |
(if index |
|
1663 |
(helm-append-at-nth actions new-action index) |
|
1664 |
(append actions new-action)) |
|
1665 |
source))) |
|
1666 |
|
|
1667 |
(defun helm-delete-action-from-source (action-or-name source) |
|
1668 |
"Delete ACTION-OR-NAME from SOURCE. |
|
1669 |
ACTION-OR-NAME can either be the name of action or the symbol function |
|
1670 |
associated to name." |
|
1671 |
(let* ((actions (helm-attr 'action source 'ignorefn)) |
|
1672 |
(del-action (if (symbolp action-or-name) |
|
1673 |
(rassoc action-or-name actions) |
|
1674 |
(assoc action-or-name actions)))) |
|
1675 |
(helm-attrset 'action (delete del-action actions) source))) |
|
1676 |
|
|
1677 |
(cl-defun helm-add-action-to-source-if (name fn source predicate |
|
1678 |
&optional (index 4) test-only) |
|
1679 |
"Add new action NAME linked to function FN to SOURCE. |
|
1680 |
Action NAME will be available when the current candidate matches PREDICATE. |
|
1681 |
This function adds an entry in the `action-transformer' attribute |
|
1682 |
of SOURCE (or creates one if not found). |
|
1683 |
Function PREDICATE must take one candidate as arg. |
|
1684 |
Function FN should be a valid function that takes one arg i.e. candidate, |
|
1685 |
argument NAME is a string that will appear in action menu |
|
1686 |
and SOURCE should be an existing helm source already loaded. |
|
1687 |
If INDEX is specified, action is added in action list at INDEX. |
|
1688 |
Value of INDEX should be always >=1, default to 4. |
|
1689 |
This allow user to add a specific `action-transformer' |
|
1690 |
to an existing source without modifying source code. |
|
1691 |
E.g |
|
1692 |
Add the action \"Byte compile file async\" linked to |
|
1693 |
function 'async-byte-compile-file to source `helm-source-find-files' |
|
1694 |
only when predicate helm-ff-candidates-lisp-p return non-`nil': |
|
1695 |
|
|
1696 |
\(helm-add-action-to-source-if \"Byte compile file async\" |
|
1697 |
'async-byte-compile-file |
|
1698 |
helm-source-find-files |
|
1699 |
'helm-ff-candidates-lisp-p\)." |
|
1700 |
(let* ((actions (helm-attr 'action source 'ignorefn)) |
|
1701 |
(action-transformers (helm-attr 'action-transformer source)) |
|
1702 |
(new-action (list (cons name fn))) |
|
1703 |
(transformer (lambda (actions candidate) |
|
1704 |
(cond ((funcall predicate candidate) |
|
1705 |
(helm-append-at-nth |
|
1706 |
actions new-action index)) |
|
1707 |
(t actions))))) |
|
1708 |
(when (functionp actions) |
|
1709 |
(helm-attrset 'action (list (cons "Default action" actions)) source)) |
|
1710 |
(when (or (symbolp action-transformers) (functionp action-transformers)) |
|
1711 |
(setq action-transformers (list action-transformers))) |
|
1712 |
(if test-only ; debug |
|
1713 |
(delq nil (append (list transformer) action-transformers)) |
|
1714 |
(helm-attrset 'action-transformer |
|
1715 |
(helm-fast-remove-dups |
|
1716 |
(delq nil (append (list transformer) action-transformers)) |
|
1717 |
:test 'equal) |
|
1718 |
source)))) |
|
1719 |
|
|
1720 |
|
|
1721 |
;;; Source filter |
|
1722 |
;; |
|
1723 |
(defun helm-set-source-filter (sources) |
|
1724 |
"Set the value of `helm-source-filter' to SOURCES and update. |
|
1725 |
|
|
1726 |
This function sets a filter for helm sources and it may be |
|
1727 |
called while helm is running. It can be used to toggle |
|
1728 |
displaying of sources dynamically. For example, additional keys |
|
1729 |
can be bound into `helm-map' to display only the file-related |
|
1730 |
results if there are too many matches from other sources and |
|
1731 |
you're after files only: |
|
1732 |
|
|
1733 |
Shift+F shows only file results from some sources: |
|
1734 |
|
|
1735 |
\(define-key helm-map \"F\" 'helm-my-show-files-only) |
|
1736 |
|
|
1737 |
\(defun helm-my-show-files-only () |
|
1738 |
(interactive) |
|
1739 |
(helm-set-source-filter '(\"File Name History\" |
|
1740 |
\"Files from Current Directory\"))) |
|
1741 |
|
|
1742 |
Shift+A shows all results: |
|
1743 |
|
|
1744 |
\(define-key helm-map \"A\" 'helm-my-show-all) |
|
1745 |
|
|
1746 |
\(defun helm-my-show-all () |
|
1747 |
(interactive) |
|
1748 |
(helm-set-source-filter nil)) |
|
1749 |
|
|
1750 |
The -my- part is added to avoid collisions with |
|
1751 |
existing Helm function names." |
|
1752 |
(with-helm-buffer |
|
1753 |
(let ((cur-disp-sel (helm-get-selection nil t))) |
|
1754 |
(set (make-local-variable 'helm-source-filter) |
|
1755 |
(helm--normalize-filter-sources sources)) |
|
1756 |
(helm-log "helm-source-filter = %S" helm-source-filter) |
|
1757 |
;; Use force-update to run init/update functions. |
|
1758 |
(helm-force-update (and (stringp cur-disp-sel) |
|
1759 |
(regexp-quote cur-disp-sel)))))) |
|
1760 |
|
|
1761 |
(defun helm--normalize-filter-sources (sources) |
|
1762 |
(cl-loop for s in sources collect |
|
1763 |
(cl-typecase s |
|
1764 |
(symbol (assoc-default 'name (symbol-value s))) |
|
1765 |
(list (assoc-default 'name s)) |
|
1766 |
(string s)))) |
|
1767 |
|
|
1768 |
(defun helm-set-sources (sources &optional no-init no-update) |
|
1769 |
"Set SOURCES during `helm' invocation. |
|
1770 |
If NO-INIT is non-`nil', skip executing init functions of SOURCES. |
|
1771 |
If NO-UPDATE is non-`nil', skip executing `helm-update'." |
|
1772 |
(with-current-buffer helm-buffer |
|
1773 |
(setq helm-sources (helm-get-sources sources)) |
|
1774 |
(helm-log "helm-sources = %S" helm-sources)) |
|
1775 |
(unless no-init (helm-compute-attr-in-sources 'init)) |
|
1776 |
(unless no-update (helm-update))) |
|
1777 |
|
|
1778 |
(defun helm-get-selection (&optional buffer force-display-part source) |
|
1779 |
"Return the currently selected item or nil. |
|
1780 |
|
|
1781 |
if BUFFER is nil or unspecified, use helm-buffer as default value. |
|
1782 |
If FORCE-DISPLAY-PART is non-`nil', return the display string. |
|
1783 |
If FORCE-DISPLAY-PART value is `withprop' the display string is returned |
|
1784 |
with its properties." |
|
1785 |
(setq buffer (or buffer helm-buffer)) |
|
1786 |
(unless (or (helm-empty-buffer-p buffer) |
|
1787 |
(helm-pos-header-line-p)) |
|
1788 |
(with-current-buffer buffer |
|
1789 |
(let* ((disp-fn (if (eq force-display-part 'withprop) |
|
1790 |
'buffer-substring |
|
1791 |
'buffer-substring-no-properties)) |
|
1792 |
(selection |
|
1793 |
(or (and (not force-display-part) |
|
1794 |
(get-text-property (overlay-start |
|
1795 |
helm-selection-overlay) |
|
1796 |
'helm-realvalue)) |
|
1797 |
;; It is needed to return properties of DISP in some case, |
|
1798 |
;; e.g for `helm-confirm-and-exit-minibuffer', |
|
1799 |
;; so use `buffer-substring' here when 'withprop is specified. |
|
1800 |
(let* ((beg (overlay-start helm-selection-overlay)) |
|
1801 |
(end (overlay-end helm-selection-overlay)) |
|
1802 |
;; If there is no selection at point, the |
|
1803 |
;; overlay is at its initial pos, (point-min) |
|
1804 |
;; (point-min), that's mean the helm-buffer |
|
1805 |
;; is not empty but have no selection yet, |
|
1806 |
;; this happen with grep sentinel sending an |
|
1807 |
;; error message in helm-buffer when no matches. |
|
1808 |
(disp (unless (= beg end) (funcall disp-fn beg (1- end)))) |
|
1809 |
(src (or source (helm-get-current-source)))) |
|
1810 |
(helm-aif (and src disp |
|
1811 |
(not force-display-part) |
|
1812 |
(assoc-default 'display-to-real src)) |
|
1813 |
(helm-apply-functions-from-source source it disp) |
|
1814 |
disp))))) |
|
1815 |
(unless (equal selection "") |
|
1816 |
(helm-log "selection = %S" selection) |
|
1817 |
selection))))) |
|
1818 |
|
|
1819 |
(defun helm-get-actions-from-current-source (&optional source) |
|
1820 |
"Return the associated action for the selected candidate. |
|
1821 |
It is a function symbol (sole action) or list |
|
1822 |
of (action-display . function)." |
|
1823 |
(unless (helm-empty-buffer-p (helm-buffer-get)) |
|
1824 |
(let ((src (helm-get-current-source))) |
|
1825 |
(helm-aif (helm-attr 'action-transformer) |
|
1826 |
(helm-apply-functions-from-source |
|
1827 |
(or source src) it |
|
1828 |
(helm-attr 'action nil 'ignorefn) |
|
1829 |
;; Check if the first given transformer |
|
1830 |
;; returns the same set of actions for each |
|
1831 |
;; candidate in marked candidates. |
|
1832 |
;; If so use the car of marked to determine |
|
1833 |
;; the set of actions, otherwise use the selection. |
|
1834 |
(if (cl-loop with marked = (helm-marked-candidates) |
|
1835 |
with act = (car (helm-mklist it)) |
|
1836 |
with acts = (funcall act nil (car marked)) |
|
1837 |
for c in marked |
|
1838 |
always (equal (funcall act nil c) acts)) |
|
1839 |
(car (helm-marked-candidates)) |
|
1840 |
(helm-get-selection nil nil src))) |
|
1841 |
(helm-attr 'action nil 'ignorefn))))) |
|
1842 |
|
|
1843 |
(defun helm-get-current-source () |
|
1844 |
"Return the source for the current selection. |
|
1845 |
Return nil when `helm-buffer' is empty." |
|
1846 |
(or helm-current-source |
|
1847 |
(with-helm-buffer |
|
1848 |
(or (get-text-property (point) 'helm-cur-source) |
|
1849 |
(progn |
|
1850 |
;; This is needed to not loose selection. |
|
1851 |
(goto-char (overlay-start helm-selection-overlay)) |
|
1852 |
(let ((header-pos (or (helm-get-previous-header-pos) |
|
1853 |
(helm-get-next-header-pos)))) |
|
1854 |
;; Return nil when no--candidates. |
|
1855 |
(when header-pos |
|
1856 |
(cl-loop with source-name = (save-excursion |
|
1857 |
(goto-char header-pos) |
|
1858 |
(helm-current-line-contents)) |
|
1859 |
for source in helm-sources thereis |
|
1860 |
(and (equal (assoc-default 'name source) source-name) |
|
1861 |
source))))))))) |
|
1862 |
|
|
1863 |
(defun helm-buffer-is-modified (buffer) |
|
1864 |
"Return non-`nil' when BUFFER is modified since `helm' was invoked." |
|
1865 |
(let* ((buf (get-buffer buffer)) |
|
1866 |
(key (concat (buffer-name buf) "/" (helm-attr 'name))) |
|
1867 |
(source-tick (or (gethash key helm-tick-hash) 0)) |
|
1868 |
(buffer-tick (buffer-chars-modified-tick buf)) |
|
1869 |
(modifiedp (/= source-tick buffer-tick))) |
|
1870 |
(puthash key buffer-tick helm-tick-hash) |
|
1871 |
(helm-log "buffer = %S" buffer) |
|
1872 |
(helm-log "modifiedp = %S" modifiedp) |
|
1873 |
modifiedp)) |
|
1874 |
|
|
1875 |
(defun helm-current-buffer-is-modified () |
|
1876 |
"Check if `helm-current-buffer' is modified since `helm' was invoked." |
|
1877 |
(helm-buffer-is-modified helm-current-buffer)) |
|
1878 |
|
|
1879 |
(defun helm-run-after-exit (function &rest args) |
|
1880 |
"Execute FUNCTION with ARGS after exiting `helm'. |
|
1881 |
The action is to call FUNCTION with arguments ARGS. |
|
1882 |
Unlike `helm-exit-and-execute-action', this can be used |
|
1883 |
to call non--actions functions with any ARGS or no ARGS at all. |
|
1884 |
|
|
1885 |
Use this on commands invoked from key-bindings, but not |
|
1886 |
on action functions invoked as action from the action menu, |
|
1887 |
i.e. functions called with RET." |
|
1888 |
(helm-kill-async-processes) |
|
1889 |
(helm-log "function = %S" function) |
|
1890 |
(helm-log "args = %S" args) |
|
1891 |
(helm-exit-and-execute-action |
|
1892 |
(lambda (_candidate) |
|
1893 |
(apply function args)))) |
|
1894 |
|
|
1895 |
(defun helm-exit-and-execute-action (action) |
|
1896 |
"Exit current helm session and execute ACTION. |
|
1897 |
Argument ACTION is a function called with one arg (candidate) |
|
1898 |
and part of the actions of current source. |
|
1899 |
|
|
1900 |
Use this on commands invoked from key-bindings, but not |
|
1901 |
on action functions invoked as action from the action menu, |
|
1902 |
i.e functions called with RET." |
|
1903 |
;; If ACTION is not an action available in source 'action attribute, |
|
1904 |
;; return an error. This allow to remove unneeded actions from |
|
1905 |
;; source that inherit actions from type, note that ACTION have to |
|
1906 |
;; be bound to a symbol and not to be an anonymous action |
|
1907 |
;; i.e. lambda or byte-code. |
|
1908 |
(let ((actions (helm-attr 'action nil t))) |
|
1909 |
(when actions |
|
1910 |
(cl-assert (or (eq action actions) |
|
1911 |
(rassq action actions) |
|
1912 |
;; Compiled lambda |
|
1913 |
(byte-code-function-p action) |
|
1914 |
;; Lambdas |
|
1915 |
(and (listp action) (functionp action))) |
|
1916 |
nil "No such action `%s' for this source" action))) |
|
1917 |
(setq helm-saved-action action) |
|
1918 |
(setq helm-saved-selection (or (helm-get-selection) "")) |
|
1919 |
(setq helm--executing-helm-action t) |
|
1920 |
;; When toggling minibuffer and header-line, we want next action |
|
1921 |
;; inherit this setting. |
|
1922 |
(helm-set-local-variable 'helm-echo-input-in-header-line |
|
1923 |
(with-helm-buffer helm-echo-input-in-header-line)) |
|
1924 |
;; Ensure next action use same display function as initial helm-buffer when |
|
1925 |
;; helm-actions-inherit-frame-settings is non nil. |
|
1926 |
(when (and helm-actions-inherit-frame-settings |
|
1927 |
helm--buffer-in-new-frame-p) |
|
1928 |
(helm-set-local-variable 'helm-display-function |
|
1929 |
(with-helm-buffer helm-display-function) |
|
1930 |
'helm--last-frame-parameters |
|
1931 |
(with-helm-buffer |
|
1932 |
(helm--get-frame-parameters))) |
|
1933 |
;; The helm-buffer keeps `helm-display-function' and |
|
1934 |
;; `helm--get-frame-parameters' values during 0.5 seconds, just |
|
1935 |
;; the time to execute the possible helm action with those values. |
|
1936 |
;; If no helm based action run within 0.5 seconds, the next helm |
|
1937 |
;; session will have to resolve again those variable values. |
|
1938 |
(run-with-idle-timer 0.5 nil |
|
1939 |
(lambda () (helm-set-local-variable 'helm-display-function nil |
|
1940 |
'helm--last-frame-parameters nil)))) |
|
1941 |
(helm-exit-minibuffer)) |
|
1942 |
|
|
1943 |
(defun helm--get-frame-parameters (&optional frame) |
|
1944 |
(cl-loop with params = (frame-parameters frame) |
|
1945 |
for p in helm--frame-default-attributes |
|
1946 |
when (assq p params) collect it)) |
|
1947 |
|
|
1948 |
(defalias 'helm-run-after-quit 'helm-run-after-exit) |
|
1949 |
(make-obsolete 'helm-run-after-quit 'helm-run-after-exit "1.7.7") |
|
1950 |
(defalias 'helm-quit-and-execute-action 'helm-exit-and-execute-action) |
|
1951 |
(make-obsolete 'helm-quit-and-execute-action 'helm-exit-and-execute-action "1.7.7") |
|
1952 |
|
|
1953 |
(defun helm-interpret-value (value &optional source compute) |
|
1954 |
"Interpret VALUE as variable, function or literal and return it. |
|
1955 |
If VALUE is a function, call it with no arguments and return the value |
|
1956 |
unless COMPUTE value is 'ignorefn. |
|
1957 |
If SOURCE compute VALUE for this source. |
|
1958 |
If VALUE is a variable, return the value. |
|
1959 |
If VALUE is a symbol, but it is not a function or a variable, cause an error. |
|
1960 |
Otherwise, return VALUE itself." |
|
1961 |
(cond ((and source (functionp value) (not (eq compute 'ignorefn))) |
|
1962 |
(helm-apply-functions-from-source source value)) |
|
1963 |
((and (functionp value) (not (eq compute 'ignorefn))) |
|
1964 |
(funcall value)) |
|
1965 |
((and (symbolp value) (boundp value)) |
|
1966 |
(symbol-value value)) |
|
1967 |
((and (symbolp value) (not (functionp value))) |
|
1968 |
(error |
|
1969 |
"helm-interpret-value: Symbol must be a function or a variable")) |
|
1970 |
(t |
|
1971 |
value))) |
|
1972 |
|
|
1973 |
(defun helm-set-local-variable (&rest args) |
|
1974 |
"Bind each pair in ARGS locally to `helm-buffer'. |
|
1975 |
|
|
1976 |
Use this to set local vars before calling helm. |
|
1977 |
|
|
1978 |
When used from an init or update function |
|
1979 |
(i.e when `helm-force-update' is running) the variables are set |
|
1980 |
using `make-local-variable' within the `helm-buffer'. |
|
1981 |
|
|
1982 |
Usage: helm-set-local-variable ([VAR VALUE]...) |
|
1983 |
Just like `setq' except that the vars are not set sequentially. |
|
1984 |
IOW Don't use VALUE of previous VAR to set the VALUE of next VAR. |
|
1985 |
|
|
1986 |
\(fn VAR VALUE ...)" |
|
1987 |
(if helm--force-updating-p |
|
1988 |
(with-helm-buffer |
|
1989 |
(cl-loop for i on args by #'cddr |
|
1990 |
do (set (make-local-variable (car i)) (cadr i)))) |
|
1991 |
(setq helm--local-variables |
|
1992 |
(append (cl-loop for i on args by #'cddr |
|
1993 |
collect (cons (car i) (cadr i))) |
|
1994 |
helm--local-variables)))) |
|
1995 |
|
|
1996 |
(defun helm--set-local-variables-internal () |
|
1997 |
(cl-loop for (var . val) in helm--local-variables |
|
1998 |
;; If `helm-set-local-variable' is called twice or more |
|
1999 |
;; on same variable use the last value entered which is |
|
2000 |
;; the first on stack e.g. |
|
2001 |
;; (helm-set-local-variable 'helm-foo 1) |
|
2002 |
;; (helm-set-local-variable 'helm-foo 2) |
|
2003 |
;; helm--local-variables => |
|
2004 |
;; '((helm-foo . 2) (helm-foo. 1)) |
|
2005 |
;; (helm-foo . 2) is retained and (helm-foo . 1) |
|
2006 |
;; ignored. |
|
2007 |
unless (memq var computed) |
|
2008 |
do (set (make-local-variable var) val) |
|
2009 |
collect var into computed |
|
2010 |
finally (setq helm--local-variables nil))) |
|
2011 |
|
|
2012 |
|
|
2013 |
;; API helper |
|
2014 |
(cl-defun helm-empty-buffer-p (&optional (buffer helm-buffer)) |
|
2015 |
"Check if BUFFER have candidates. |
|
2016 |
Default value for BUFFER is `helm-buffer'." |
|
2017 |
(zerop (buffer-size (and buffer (get-buffer buffer))))) |
|
2018 |
|
|
2019 |
(defun helm-empty-source-p () |
|
2020 |
"Check if current source contains candidates. |
|
2021 |
This could happen when for example the last element of a source |
|
2022 |
was deleted and the candidates list not updated." |
|
2023 |
(when (helm-window) |
|
2024 |
(with-helm-window |
|
2025 |
(or (helm-empty-buffer-p) |
|
2026 |
(and (helm-end-of-source-p) |
|
2027 |
(eq (point-at-bol) (point-at-eol)) |
|
2028 |
(or |
|
2029 |
(save-excursion |
|
2030 |
(forward-line -1) |
|
2031 |
(helm-pos-header-line-p)) |
|
2032 |
(bobp))))))) |
|
2033 |
|
|
2034 |
|
|
2035 |
;; Tools |
|
2036 |
;; |
|
2037 |
(defun helm-apply-functions-from-source (source functions &rest args) |
|
2038 |
"From SOURCE apply FUNCTIONS on ARGS. |
|
2039 |
|
|
2040 |
This function is used to process filter functions, when filter is a |
|
2041 |
\`filtered-candidate-transformer', we pass to ARGS candidates+source |
|
2042 |
whereas when the filter is `candidate-transformer' we pass to ARGS |
|
2043 |
candidates only. |
|
2044 |
This function is used also to process functions called with no args, |
|
2045 |
e.g. init functions, in this case it is called without ARGS. |
|
2046 |
See `helm-process-filtered-candidate-transformer' |
|
2047 |
\`helm-compute-attr-in-sources' and |
|
2048 |
\`helm-process-candidate-transformer'. |
|
2049 |
|
|
2050 |
Arg FUNCTIONS is either a symbol or a list of functions, each function being |
|
2051 |
applied on ARGS and called on the result of the precedent function. |
|
2052 |
Return the result of last function call." |
|
2053 |
(let ((helm--source-name (assoc-default 'name source)) |
|
2054 |
(helm-current-source source) |
|
2055 |
(funs (if (functionp functions) (list functions) functions))) |
|
2056 |
(helm-log "helm--source-name = %S" helm--source-name) |
|
2057 |
(helm-log "functions = %S" functions) |
|
2058 |
(helm-log "args = %S" args) |
|
2059 |
(cl-loop with result |
|
2060 |
for fn in funs |
|
2061 |
do (setq result (apply fn args)) |
|
2062 |
when (and args (cdr funs)) |
|
2063 |
;; In filter functions, ARGS is a list of one or two elements where |
|
2064 |
;; the first element is the list of candidates and the second |
|
2065 |
;; a list containing the source. |
|
2066 |
;; When more than one fn, set the candidates list to what returns |
|
2067 |
;; this fn to compute the modified candidates with the next fn |
|
2068 |
;; and so on. |
|
2069 |
do (setcar args result) |
|
2070 |
finally return result))) |
|
2071 |
|
|
2072 |
(defalias 'helm-funcall-with-source 'helm-apply-functions-from-source) |
|
2073 |
(make-obsolete 'helm-funcall-with-source 'helm-apply-functions-from-source "2.9.7") |
|
2074 |
|
|
2075 |
(defun helm-compute-attr-in-sources (attr &optional sources) |
|
2076 |
"Call the associated function(s) to ATTR for each source if any." |
|
2077 |
(let ((sources (or (helm-get-sources sources) |
|
2078 |
;; Fix error no buffer named *helm... by checking |
|
2079 |
;; if helm-buffer exists. |
|
2080 |
(and (buffer-live-p (get-buffer (helm-buffer-get))) |
|
2081 |
;; `helm-sources' are local to helm-buffer. |
|
2082 |
(with-helm-buffer helm-sources))))) |
|
2083 |
(when sources |
|
2084 |
(cl-dolist (source sources) |
|
2085 |
(helm-aif (assoc-default attr source) |
|
2086 |
(helm-apply-functions-from-source source it)))))) |
|
2087 |
|
|
2088 |
(defalias 'helm-funcall-foreach 'helm-compute-attr-in-sources) |
|
2089 |
(make-obsolete 'helm-funcall-foreach 'helm-compute-attr-in-sources "2.9.7") |
|
2090 |
|
|
2091 |
(defun helm-normalize-sources (sources) |
|
2092 |
"If SOURCES is only one source, make a list of one element." |
|
2093 |
(if (or (and sources (symbolp sources)) |
|
2094 |
(and (listp sources) (assq 'name sources))) |
|
2095 |
(list sources) |
|
2096 |
sources)) |
|
2097 |
|
|
2098 |
(defun helm-get-candidate-number (&optional in-current-source) |
|
2099 |
"Return candidates number in `helm-buffer'. |
|
2100 |
If IN-CURRENT-SOURCE is provided return number of candidates of current source |
|
2101 |
only." |
|
2102 |
(with-helm-buffer |
|
2103 |
(if (or (helm-empty-buffer-p) |
|
2104 |
(helm-empty-source-p)) |
|
2105 |
0 |
|
2106 |
(save-excursion |
|
2107 |
(helm-aif (and in-current-source (helm-get-previous-header-pos)) |
|
2108 |
(goto-char it) |
|
2109 |
(goto-char (point-min))) |
|
2110 |
(forward-line 1) |
|
2111 |
(if (helm-pos-multiline-p) |
|
2112 |
(cl-loop with count-multi = 1 |
|
2113 |
while (and (not (if in-current-source |
|
2114 |
(save-excursion |
|
2115 |
(forward-line 2) |
|
2116 |
(or (helm-pos-header-line-p) (eobp))) |
|
2117 |
(eobp))) |
|
2118 |
(search-forward helm-candidate-separator nil t)) |
|
2119 |
do (cl-incf count-multi) |
|
2120 |
finally return count-multi) |
|
2121 |
(cl-loop with ln = 0 |
|
2122 |
while (not (if in-current-source |
|
2123 |
(or (helm-pos-header-line-p) (eobp)) |
|
2124 |
(eobp))) |
|
2125 |
;; Don't count empty lines maybe added by popup (#1370). |
|
2126 |
unless (or (eq (point-at-bol) (point-at-eol)) |
|
2127 |
(helm-pos-header-line-p)) |
|
2128 |
do (cl-incf ln) |
|
2129 |
do (forward-line 1) finally return ln)))))) |
|
2130 |
|
|
2131 |
(defmacro with-helm-quittable (&rest body) |
|
2132 |
"If an error occurs in execution of BODY, safely quit helm." |
|
2133 |
(declare (indent 0) (debug t)) |
|
2134 |
`(condition-case _v |
|
2135 |
(let (inhibit-quit) |
|
2136 |
,@body) |
|
2137 |
(quit (setq quit-flag t) |
|
2138 |
(setq helm-quit t) |
|
2139 |
(exit-minibuffer) |
|
2140 |
(keyboard-quit) |
|
2141 |
;; See comment about this in `with-local-quit'. |
|
2142 |
(eval '(ignore nil))))) |
|
2143 |
|
|
2144 |
;; Entry point |
|
2145 |
;; `:allow-nest' is not in this list because it is treated before. |
|
2146 |
(defconst helm-argument-keys |
|
2147 |
'(:sources :input :prompt :resume |
|
2148 |
:preselect :buffer :keymap :default :history)) |
|
2149 |
|
|
2150 |
;;;###autoload |
|
2151 |
(defun helm (&rest plist) |
|
2152 |
"Main function to execute helm sources. |
|
2153 |
|
|
2154 |
PLIST is a list like |
|
2155 |
|
|
2156 |
\(:key1 val1 :key2 val2 ...\) |
|
2157 |
|
|
2158 |
or |
|
2159 |
|
|
2160 |
\(&optional sources input prompt resume preselect |
|
2161 |
buffer keymap default history allow-nest\). |
|
2162 |
|
|
2163 |
** Keywords |
|
2164 |
|
|
2165 |
Keywords supported: |
|
2166 |
|
|
2167 |
- :sources |
|
2168 |
- :input |
|
2169 |
- :prompt |
|
2170 |
- :resume |
|
2171 |
- :preselect |
|
2172 |
- :buffer |
|
2173 |
- :keymap |
|
2174 |
- :default |
|
2175 |
- :history |
|
2176 |
- :allow-nest |
|
2177 |
|
|
2178 |
Extra LOCAL-VARS keywords are supported, see the \"** Other |
|
2179 |
keywords\" section below. |
|
2180 |
|
|
2181 |
Basic keywords are the following: |
|
2182 |
|
|
2183 |
*** :sources |
|
2184 |
|
|
2185 |
One of the following: |
|
2186 |
|
|
2187 |
- List of sources |
|
2188 |
- Symbol whose value is a list of sources |
|
2189 |
- Alist representing a Helm source. |
|
2190 |
- In this case the source has no name and is referenced in |
|
2191 |
`helm-sources' as a whole alist. |
|
2192 |
|
|
2193 |
*** :input |
|
2194 |
|
|
2195 |
Initial input of minibuffer (temporary value of `helm-pattern') |
|
2196 |
|
|
2197 |
*** :prompt |
|
2198 |
|
|
2199 |
Minibuffer prompt. Default value is `helm--prompt'. |
|
2200 |
|
|
2201 |
*** :resume |
|
2202 |
|
|
2203 |
If t, allow resumption of the previous session of this Helm |
|
2204 |
command, skipping initialization. |
|
2205 |
|
|
2206 |
If 'noresume, this instance of `helm' cannot be resumed. |
|
2207 |
|
|
2208 |
*** :preselect |
|
2209 |
|
|
2210 |
Initially selected candidate (string or regexp). |
|
2211 |
|
|
2212 |
*** :buffer |
|
2213 |
|
|
2214 |
Buffer name for this Helm session. `helm-buffer' will take this value. |
|
2215 |
|
|
2216 |
*** :keymap |
|
2217 |
|
|
2218 |
\[Obsolete] |
|
2219 |
|
|
2220 |
Keymap used at the start of this Helm session. |
|
2221 |
|
|
2222 |
It is overridden by keymaps specified in sources, and is kept |
|
2223 |
only for backward compatibility. |
|
2224 |
|
|
2225 |
Keymaps should be specified in sources using the :keymap slot |
|
2226 |
instead. See `helm-source'. |
|
2227 |
|
|
2228 |
This keymap is not restored by `helm-resume'. |
|
2229 |
|
|
2230 |
*** :default |
|
2231 |
|
|
2232 |
Default value inserted into the minibuffer \ with |
|
2233 |
\\<minibuffer-local-map>\\[next-history-element]. |
|
2234 |
|
|
2235 |
It can be a string or a list of strings, in this case |
|
2236 |
\\<minibuffer-local-map>\\[next-history-element] cycles through |
|
2237 |
the list items, starting with the first. |
|
2238 |
|
|
2239 |
If nil, `thing-at-point' is used. |
|
2240 |
|
|
2241 |
If `helm--maybe-use-default-as-input' is non-`nil', display is |
|
2242 |
updated using this value, unless :input is specified, in which |
|
2243 |
case that value is used instead. |
|
2244 |
|
|
2245 |
*** :history |
|
2246 |
|
|
2247 |
Minibuffer input, by default, is pushed to `minibuffer-history'. |
|
2248 |
|
|
2249 |
When an argument HISTORY is provided, input is pushed to |
|
2250 |
HISTORY. HISTORY should be a valid symbol. |
|
2251 |
|
|
2252 |
*** :allow-nest |
|
2253 |
|
|
2254 |
Allow running this Helm command in a running Helm session. |
|
2255 |
|
|
2256 |
** Other keywords |
|
2257 |
|
|
2258 |
Other keywords are interpreted as local variables of this Helm |
|
2259 |
session. The `helm-' prefix can be omitted. For example, |
|
2260 |
|
|
2261 |
\(helm :sources 'helm-source-buffers-list |
|
2262 |
:buffer \"*helm buffers*\" |
|
2263 |
:candidate-number-limit 10\) |
|
2264 |
|
|
2265 |
starts a Helm session with the variable |
|
2266 |
`helm-candidate-number-limit' set to 10. |
|
2267 |
|
|
2268 |
** Backward compatibility |
|
2269 |
|
|
2270 |
For backward compatibility, positional parameters are |
|
2271 |
supported: |
|
2272 |
|
|
2273 |
\(helm sources input prompt resume preselect |
|
2274 |
buffer keymap default history allow-nest\) |
|
2275 |
|
|
2276 |
However, the use of non-keyword args is deprecated. |
|
2277 |
|
|
2278 |
\(fn &key SOURCES INPUT PROMPT RESUME PRESELECT BUFFER KEYMAP DEFAULT HISTORY ALLOW-NEST OTHER-LOCAL-VARS)" |
|
2279 |
(let ((fn (cond ((or (and helm-alive-p (plist-get plist :allow-nest)) |
|
2280 |
(and helm-alive-p (memq 'allow-nest plist))) |
|
2281 |
#'helm--nest) |
|
2282 |
((keywordp (car plist)) |
|
2283 |
#'helm) |
|
2284 |
(t #'helm-internal)))) |
|
2285 |
(if (and helm-alive-p (eq fn #'helm)) |
|
2286 |
(if (helm--alive-p) |
|
2287 |
;; A helm session is normally running. |
|
2288 |
(error "Error: Trying to run helm within a running helm session") |
|
2289 |
;; A helm session is already running and user jump somewhere else |
|
2290 |
;; without deactivating it. |
|
2291 |
(with-helm-buffer |
|
2292 |
(prog1 |
|
2293 |
(message "Aborting an helm session running in background") |
|
2294 |
;; `helm-alive-p' will be reset in unwind-protect forms. |
|
2295 |
(helm-keyboard-quit)))) |
|
2296 |
(if (keywordp (car plist)) |
|
2297 |
;; Parse `plist' and move not regular `helm-argument-keys' |
|
2298 |
;; to `helm--local-variables', then calling helm on itself |
|
2299 |
;; with normal arguments (the non--arguments-keys removed) |
|
2300 |
;; will end up in [1]. |
|
2301 |
(progn |
|
2302 |
(setq helm--local-variables |
|
2303 |
(append helm--local-variables |
|
2304 |
;; Vars passed by keyword on helm call |
|
2305 |
;; take precedence on same vars |
|
2306 |
;; that may have been passed before helm call. |
|
2307 |
(helm-parse-keys plist))) |
|
2308 |
(apply fn (mapcar (lambda (key) (plist-get plist key)) |
|
2309 |
helm-argument-keys))) |
|
2310 |
(apply fn plist))))) ; [1] fn == helm-internal. |
|
2311 |
|
|
2312 |
(defun helm--alive-p () |
|
2313 |
"[Internal] Check if `helm' is alive. |
|
2314 |
An `helm' session is considered alive if `helm-alive-p' value is |
|
2315 |
non-`nil', the `helm-buffer' is visible, and cursor is in the |
|
2316 |
minibuffer." |
|
2317 |
(and helm-alive-p |
|
2318 |
(get-buffer-window (helm-buffer-get) 'visible) |
|
2319 |
(minibuffer-window-active-p (minibuffer-window)) |
|
2320 |
(minibufferp (current-buffer)))) |
|
2321 |
|
|
2322 |
(defun helm-parse-keys (keys) |
|
2323 |
"Parse the KEYS arguments of `helm'. |
|
2324 |
Return only those keys not in `helm-argument-keys', prefix them |
|
2325 |
with \"helm\", and then convert them to an alist. This allows |
|
2326 |
adding arguments that are not part of `helm-argument-keys', but |
|
2327 |
are valid helm variables nevertheless. For |
|
2328 |
example, :candidate-number-limit is bound to |
|
2329 |
`helm-candidate-number-limit' in the source. |
|
2330 |
|
|
2331 |
(helm-parse-keys '(:sources ((name . \"test\") |
|
2332 |
(candidates . (a b c))) |
|
2333 |
:buffer \"toto\" |
|
2334 |
:candidate-number-limit 4)) |
|
2335 |
==> ((helm-candidate-number-limit . 4))." |
|
2336 |
|
|
2337 |
(cl-loop for (key value) on keys by #'cddr |
|
2338 |
for symname = (substring (symbol-name key) 1) |
|
2339 |
for sym = (intern (if (string-match "^helm-" symname) |
|
2340 |
symname |
|
2341 |
(concat "helm-" symname))) |
|
2342 |
unless (memq key helm-argument-keys) |
|
2343 |
collect (cons sym value))) |
|
2344 |
|
|
2345 |
;;; Entry point helper |
|
2346 |
(defun helm-internal (&optional |
|
2347 |
any-sources any-input |
|
2348 |
any-prompt any-resume |
|
2349 |
any-preselect any-buffer |
|
2350 |
any-keymap any-default any-history) |
|
2351 |
"The internal helm function called by `helm'. |
|
2352 |
For ANY-SOURCES ANY-INPUT ANY-PROMPT ANY-RESUME ANY-PRESELECT ANY-BUFFER and |
|
2353 |
ANY-KEYMAP ANY-DEFAULT ANY-HISTORY See `helm'." |
|
2354 |
(unless helm--nested (setq helm-initial-frame (selected-frame))) |
|
2355 |
;; Activate the advices. |
|
2356 |
;; Advices will be available only in >=emacs-24.4, but |
|
2357 |
;; allow compiling without errors on lower emacs. |
|
2358 |
(when (fboundp 'advice-add) |
|
2359 |
(advice-add 'tramp-read-passwd :around #'helm--suspend-read-passwd) |
|
2360 |
(advice-add 'ange-ftp-get-passwd :around #'helm--suspend-read-passwd) |
|
2361 |
(advice-add 'epa-passphrase-callback-function |
|
2362 |
:around #'helm--suspend-read-passwd) |
|
2363 |
;; Ensure linum-mode is disabled in Helm buffers to preserve |
|
2364 |
;; performances (Issue #1894). |
|
2365 |
(advice-add 'linum-on :override #'helm--advice-linum-on)) |
|
2366 |
(helm-log (concat "[Start session] " (make-string 41 ?+))) |
|
2367 |
(helm-log "any-prompt = %S" any-prompt) |
|
2368 |
(helm-log "any-preselect = %S" any-preselect) |
|
2369 |
(helm-log "any-buffer = %S" any-buffer) |
|
2370 |
(helm-log "any-keymap = %S" any-keymap) |
|
2371 |
(helm-log "any-default = %S" any-default) |
|
2372 |
(helm-log "any-history = %S" any-history) |
|
2373 |
(setq helm--prompt (or any-prompt "pattern: ")) |
|
2374 |
(let ((non-essential t) |
|
2375 |
;; Prevent mouse jumping to the upper-right |
|
2376 |
;; hand corner of the frame (#1538). |
|
2377 |
mouse-autoselect-window |
|
2378 |
focus-follows-mouse |
|
2379 |
mode-line-in-non-selected-windows |
|
2380 |
(input-method-verbose-flag helm-input-method-verbose-flag) |
|
2381 |
(helm--maybe-use-default-as-input |
|
2382 |
(and (null any-input) |
|
2383 |
(or helm--maybe-use-default-as-input ; it is let-bounded so use it. |
|
2384 |
(cl-loop for s in (helm-normalize-sources any-sources) |
|
2385 |
thereis (memq s helm-sources-using-default-as-input)))))) |
|
2386 |
(unwind-protect |
|
2387 |
(condition-case-unless-debug _v |
|
2388 |
(let ( ;; `helm--source-name' is non-`nil' |
|
2389 |
;; when `helm' is invoked by action, reset it. |
|
2390 |
helm--source-name |
|
2391 |
helm-current-source |
|
2392 |
helm-in-persistent-action |
|
2393 |
helm-quit |
|
2394 |
(helm-buffer (or any-buffer helm-buffer))) |
|
2395 |
(helm-initialize |
|
2396 |
any-resume any-input any-default any-sources) |
|
2397 |
;; We don't display helm-buffer here to avoid popping |
|
2398 |
;; up a window or a frame when exiting immediately when |
|
2399 |
;; only one candidate (this avoid having the helm frame |
|
2400 |
;; flashing), lets first compute candidates and if more |
|
2401 |
;; than one display helm-buffer (this is done later in |
|
2402 |
;; helm-read-pattern-maybe). |
|
2403 |
(unless helm-execute-action-at-once-if-one |
|
2404 |
(helm-display-buffer helm-buffer any-resume) |
|
2405 |
(select-window (helm-window))) |
|
2406 |
;; We are now in helm-buffer. |
|
2407 |
(unless helm-allow-mouse |
|
2408 |
(helm--remap-mouse-mode 1)) ; Disable mouse bindings. |
|
2409 |
(add-hook 'post-command-hook 'helm--maybe-update-keymap) |
|
2410 |
;; Add also to update hook otherwise keymap is not updated |
|
2411 |
;; until a key is hitted (Issue #1670). |
|
2412 |
(add-hook 'helm-after-update-hook 'helm--maybe-update-keymap) |
|
2413 |
(add-hook 'post-command-hook 'helm--update-header-line) |
|
2414 |
(helm-log "show prompt") |
|
2415 |
(unwind-protect |
|
2416 |
(helm-read-pattern-maybe |
|
2417 |
any-prompt any-input any-preselect |
|
2418 |
any-resume any-keymap any-default any-history) |
|
2419 |
(helm-cleanup)) |
|
2420 |
(prog1 |
|
2421 |
(unless helm-quit (helm-execute-selection-action)) |
|
2422 |
(helm-log (concat "[End session] " (make-string 41 ?-))))) |
|
2423 |
(quit |
|
2424 |
(helm-restore-position-on-quit) |
|
2425 |
(helm-log-run-hook 'helm-quit-hook) |
|
2426 |
(helm-log (concat "[End session (quit)] " (make-string 34 ?-))) |
|
2427 |
nil)) |
|
2428 |
(when (fboundp 'advice-remove) |
|
2429 |
(advice-remove 'tramp-read-passwd #'helm--suspend-read-passwd) |
|
2430 |
(advice-remove 'ange-ftp-get-passwd #'helm--suspend-read-passwd) |
|
2431 |
(advice-remove 'epa-passphrase-callback-function #'helm--suspend-read-passwd) |
|
2432 |
(advice-remove 'linum-on #'helm--advice-linum-on)) |
|
2433 |
(helm-log "helm-alive-p = %S" (setq helm-alive-p nil)) |
|
2434 |
(helm--remap-mouse-mode -1) ; Reenable mouse bindings. |
|
2435 |
(setq helm-alive-p nil) |
|
2436 |
;; Prevent error "No buffer named *helm*" triggered by |
|
2437 |
;; `helm-set-local-variable'. |
|
2438 |
(setq helm--force-updating-p nil) |
|
2439 |
(setq helm--buffer-in-new-frame-p nil) |
|
2440 |
;; Reset helm-pattern so that lambda's using it |
|
2441 |
;; before running helm will not start with its old value. |
|
2442 |
(setq helm-pattern "") |
|
2443 |
(setq helm--ignore-errors nil) |
|
2444 |
(helm-log-save-maybe)))) |
|
2445 |
|
|
2446 |
(defun helm--advice-linum-on () |
|
2447 |
(unless (or (minibufferp) |
|
2448 |
(string-match "\\`\\*helm" (buffer-name)) |
|
2449 |
(and (daemonp) (null (frame-parameter nil 'client)))) |
|
2450 |
(linum-mode 1))) |
|
2451 |
|
|
2452 |
;;; Helm resume |
|
2453 |
;; |
|
2454 |
;; |
|
2455 |
(defun helm-resume (arg) |
|
2456 |
"Resume a previous `helm' session. |
|
2457 |
Call with a prefix arg to choose among existing helm |
|
2458 |
buffers (sessions). When calling from lisp, specify a buffer-name |
|
2459 |
as a string with ARG." |
|
2460 |
(interactive "P") |
|
2461 |
(let (any-buffer |
|
2462 |
cur-dir |
|
2463 |
narrow-pos |
|
2464 |
(helm-full-frame (default-value 'helm-full-frame)) |
|
2465 |
sources) |
|
2466 |
(if arg |
|
2467 |
(if (and (stringp arg) (bufferp (get-buffer arg))) |
|
2468 |
(setq any-buffer arg) |
|
2469 |
(setq any-buffer (helm-resume-select-buffer))) |
|
2470 |
(setq any-buffer helm-last-buffer)) |
|
2471 |
(cl-assert any-buffer nil |
|
2472 |
"helm-resume: No helm buffers found to resume") |
|
2473 |
(setq sources (buffer-local-value |
|
2474 |
'helm-sources (get-buffer any-buffer))) |
|
2475 |
;; Reset `cursor-type' to nil as it have been set to t |
|
2476 |
;; when quitting previous session. |
|
2477 |
(with-current-buffer any-buffer (setq cursor-type nil)) |
|
2478 |
(setq helm-full-frame (buffer-local-value |
|
2479 |
'helm-full-frame (get-buffer any-buffer))) |
|
2480 |
(setq cur-dir (buffer-local-value |
|
2481 |
'default-directory (get-buffer any-buffer))) |
|
2482 |
(setq helm-saved-selection nil |
|
2483 |
helm-saved-action nil) |
|
2484 |
(unless (buffer-live-p helm-current-buffer) |
|
2485 |
;; `helm-current-buffer' may have been killed. |
|
2486 |
(setq helm-current-buffer (current-buffer))) |
|
2487 |
(helm-aif (with-current-buffer any-buffer |
|
2488 |
helm--current-buffer-narrowed) |
|
2489 |
(progn |
|
2490 |
(set-buffer (car it)) |
|
2491 |
(setq narrow-pos (cdr it)))) |
|
2492 |
(save-restriction |
|
2493 |
(when narrow-pos (apply #'narrow-to-region narrow-pos)) |
|
2494 |
;; Restart with same `default-directory' value this session |
|
2495 |
;; was initially started with. |
|
2496 |
(with-helm-default-directory cur-dir |
|
2497 |
(unwind-protect |
|
2498 |
(helm |
|
2499 |
:sources sources |
|
2500 |
:input (buffer-local-value 'helm-input-local (get-buffer any-buffer)) |
|
2501 |
:prompt (buffer-local-value 'helm--prompt (get-buffer any-buffer)) |
|
2502 |
:resume t |
|
2503 |
:buffer any-buffer) |
|
2504 |
(run-hook-with-args 'helm-resume-after-hook sources)))))) |
|
2505 |
|
|
2506 |
(defun helm-resume-previous-session-after-quit (arg) |
|
2507 |
"Resume previous helm session within a running helm." |
|
2508 |
(interactive "p") |
|
2509 |
(with-helm-alive-p |
|
2510 |
(if (> (length helm-buffers) arg) |
|
2511 |
(helm-run-after-exit (lambda () (helm-resume (nth arg helm-buffers)))) |
|
2512 |
(message "No previous helm sessions available for resuming!")))) |
|
2513 |
(put 'helm-resume-previous-session-after-quit 'helm-only t) |
|
2514 |
|
|
2515 |
(defun helm-resume-list-buffers-after-quit () |
|
2516 |
"List resumable helm buffers within running helm." |
|
2517 |
(interactive) |
|
2518 |
(with-helm-alive-p |
|
2519 |
(if (> (length helm-buffers) 0) |
|
2520 |
(helm-run-after-exit (lambda () (helm-resume t))) |
|
2521 |
(message "No previous helm sessions available for resuming!")))) |
|
2522 |
(put 'helm-resume-list-buffers-after-quit 'helm-only t) |
|
2523 |
|
|
2524 |
(defun helm-resume-p (any-resume) |
|
2525 |
"Whether current helm session is resumed or not." |
|
2526 |
(eq any-resume t)) |
|
2527 |
|
|
2528 |
(defun helm-resume-select-buffer () |
|
2529 |
"Select an `helm-buffer' in `helm-buffers' list to resume a helm session. |
|
2530 |
Return nil if no `helm-buffer' found." |
|
2531 |
(when helm-buffers |
|
2532 |
(or (helm :sources (helm-build-sync-source "Resume helm buffer" |
|
2533 |
:candidates helm-buffers) |
|
2534 |
:resume 'noresume |
|
2535 |
:buffer "*helm resume*") |
|
2536 |
(keyboard-quit)))) |
|
2537 |
|
|
2538 |
;;;###autoload |
|
2539 |
(defun helm-cycle-resume () |
|
2540 |
"Cycle in `helm-buffers' list and resume when waiting more than 1.2s." |
|
2541 |
(interactive) |
|
2542 |
(cl-assert (and helm-buffers helm-last-buffer) |
|
2543 |
nil "No helm buffers to resume") |
|
2544 |
;; Setup a new iterator only on first hit on |
|
2545 |
;; `helm-run-cycle-resume', subsequents hits should reuse same |
|
2546 |
;; iterator. |
|
2547 |
(unless (and (eq last-command 'helm-cycle-resume) |
|
2548 |
helm--cycle-resume-iterator) |
|
2549 |
(setq helm--cycle-resume-iterator |
|
2550 |
(helm-iter-sub-next-circular |
|
2551 |
helm-buffers helm-last-buffer :test 'equal))) |
|
2552 |
(helm--resume-or-iter)) |
|
2553 |
|
|
2554 |
(defun helm--resume-or-iter (&optional from-helm) |
|
2555 |
(message "Resuming helm buffer `%s'" helm-last-buffer) |
|
2556 |
(if (sit-for helm-cycle-resume-delay) |
|
2557 |
;; Delay expire, run helm-resume. |
|
2558 |
(if from-helm |
|
2559 |
(helm-run-after-exit (lambda () (helm-resume helm-last-buffer))) |
|
2560 |
(helm-resume helm-last-buffer)) |
|
2561 |
;; key pressed before delay, cycle. |
|
2562 |
(unless from-helm ; cycling to next item already done. |
|
2563 |
(message "Resuming helm buffer `%s'" |
|
2564 |
(setq helm-last-buffer |
|
2565 |
(helm-iter-next helm--cycle-resume-iterator)))))) |
|
2566 |
|
|
2567 |
(defun helm-run-cycle-resume () |
|
2568 |
"Same as `helm-cycle-resume' but intended to be called only from helm." |
|
2569 |
(interactive) |
|
2570 |
(when (cdr helm-buffers) ; only one session registered. |
|
2571 |
;; Setup a new iterator only on first hit on |
|
2572 |
;; `helm-run-cycle-resume', subsequents hits should reuse same |
|
2573 |
;; iterator. |
|
2574 |
(unless (and (eq last-command 'helm-run-cycle-resume) |
|
2575 |
helm--cycle-resume-iterator) |
|
2576 |
(setq helm--cycle-resume-iterator |
|
2577 |
(helm-iter-sub-next-circular |
|
2578 |
helm-buffers helm-last-buffer :test 'equal))) |
|
2579 |
;; start at next buffer as we already are at `helm-last-buffer'. |
|
2580 |
(setq helm-last-buffer |
|
2581 |
(helm-iter-next helm--cycle-resume-iterator)) |
|
2582 |
(helm--resume-or-iter 'from-helm))) |
|
2583 |
(put 'helm-run-cycle-resume 'helm-only t) |
|
2584 |
|
|
2585 |
|
|
2586 |
;;;###autoload |
|
2587 |
(defun helm-other-buffer (any-sources any-buffer) |
|
2588 |
"Simplified `helm' interface with other `helm-buffer'. |
|
2589 |
Call `helm' only with ANY-SOURCES and ANY-BUFFER as args." |
|
2590 |
(helm :sources any-sources :buffer any-buffer)) |
|
2591 |
|
|
2592 |
;;; Nested sessions |
|
2593 |
;; |
|
2594 |
;; |
|
2595 |
(defun helm--nest (&rest same-as-helm) |
|
2596 |
"[internal]Allows calling `helm' within a running helm session. |
|
2597 |
|
|
2598 |
Arguments SAME-AS-HELM are the same as `helm'. |
|
2599 |
|
|
2600 |
Don't use this directly, use instead `helm' with the keyword |
|
2601 |
:allow-nest. |
|
2602 |
|
|
2603 |
\(fn &key SOURCES INPUT PROMPT RESUME PRESELECT BUFFER KEYMAP DEFAULT HISTORY OTHER-LOCAL-VARS)" |
|
2604 |
(with-helm-window |
|
2605 |
(let ((orig-helm-current-buffer helm-current-buffer) |
|
2606 |
(orig-helm-buffer helm-buffer) |
|
2607 |
(orig-helm--prompt helm--prompt) |
|
2608 |
(orig-helm-sources helm-sources) |
|
2609 |
(orig-helm--in-fuzzy helm--in-fuzzy) |
|
2610 |
(orig-helm--display-frame helm--buffer-in-new-frame-p) |
|
2611 |
(orig-helm-last-frame-or-window-configuration |
|
2612 |
helm-last-frame-or-window-configuration) |
|
2613 |
(orig-one-window-p helm-onewindow-p) |
|
2614 |
(helm--nested t)) |
|
2615 |
;; FIXME Using helm-full-frame here allow showing the new |
|
2616 |
;; helm-buffer in the same window as old helm-buffer, why? |
|
2617 |
(helm-set-local-variable 'helm-full-frame t) |
|
2618 |
(unwind-protect |
|
2619 |
(let (helm-current-position |
|
2620 |
helm-current-buffer |
|
2621 |
helm-pattern |
|
2622 |
(helm-buffer (or (cl-getf same-as-helm :buffer) |
|
2623 |
(nth 5 same-as-helm) |
|
2624 |
"*Helm*")) |
|
2625 |
(enable-recursive-minibuffers t)) |
|
2626 |
(setq helm-sources nil) |
|
2627 |
(apply #'helm same-as-helm)) |
|
2628 |
(with-current-buffer orig-helm-buffer |
|
2629 |
(setq helm-sources orig-helm-sources) |
|
2630 |
(setq helm--nested nil) |
|
2631 |
(setq helm--buffer-in-new-frame-p orig-helm--display-frame) |
|
2632 |
(setq helm-alive-p t) ; Nested session set this to nil on exit. |
|
2633 |
(setq helm-buffer orig-helm-buffer) |
|
2634 |
(setq helm-full-frame nil) |
|
2635 |
(setq helm--prompt orig-helm--prompt) |
|
2636 |
(setq helm--in-fuzzy orig-helm--in-fuzzy) |
|
2637 |
(helm-initialize-overlays helm-buffer) |
|
2638 |
(unless (helm-empty-buffer-p) (helm-mark-current-line t)) |
|
2639 |
(setq helm-last-frame-or-window-configuration |
|
2640 |
orig-helm-last-frame-or-window-configuration) |
|
2641 |
(setq cursor-type nil) |
|
2642 |
(setq helm-current-buffer orig-helm-current-buffer) |
|
2643 |
(setq helm-onewindow-p orig-one-window-p) |
|
2644 |
;; Be sure advices, hooks, and local modes keep running. |
|
2645 |
(if (fboundp 'advice-add) |
|
2646 |
(progn |
|
2647 |
(advice-add 'tramp-read-passwd |
|
2648 |
:around #'helm--suspend-read-passwd) |
|
2649 |
(advice-add 'ange-ftp-get-passwd |
|
2650 |
:around #'helm--suspend-read-passwd) |
|
2651 |
(advice-add 'epa-passphrase-callback-function |
|
2652 |
:around #'helm--suspend-read-passwd)) |
|
2653 |
(ad-activate 'tramp-read-passwd) |
|
2654 |
(ad-activate 'ange-ftp-get-passwd)) |
|
2655 |
(unless helm-allow-mouse |
|
2656 |
(helm--remap-mouse-mode 1)) |
|
2657 |
(unless (cl-loop for h in post-command-hook |
|
2658 |
thereis (memq h '(helm--maybe-update-keymap |
|
2659 |
helm--update-header-line))) |
|
2660 |
(add-hook 'post-command-hook 'helm--maybe-update-keymap) |
|
2661 |
(add-hook 'post-command-hook 'helm--update-header-line)) |
|
2662 |
(helm-display-mode-line (helm-get-current-source))))))) |
|
2663 |
|
|
2664 |
|
|
2665 |
;;; Accessors |
|
2666 |
;; |
|
2667 |
(defun helm-current-position (save-or-restore) |
|
2668 |
"Save or restore current position in `helm-current-buffer'. |
|
2669 |
Argument SAVE-OR-RESTORE is either save or restore." |
|
2670 |
(cl-case save-or-restore |
|
2671 |
(save |
|
2672 |
(helm-log "Save position at %S" (cons (point) (window-start))) |
|
2673 |
(setq helm-current-position (cons (point) (window-start)))) |
|
2674 |
(restore |
|
2675 |
;; Maybe `helm-current-buffer' have been deleted |
|
2676 |
;; during helm session so check if it is here |
|
2677 |
;; otherwise position in underlying buffer will be lost. |
|
2678 |
(when (get-buffer-window helm-current-buffer 'visible) |
|
2679 |
(helm-log "Restore position at %S in buffer %s" |
|
2680 |
helm-current-position |
|
2681 |
(buffer-name (current-buffer))) |
|
2682 |
(goto-char (car helm-current-position)) |
|
2683 |
;; Fix this position with the NOFORCE arg of `set-window-start' |
|
2684 |
;; otherwise, if there is some other buffer than `helm-current-buffer' |
|
2685 |
;; one, position will be lost. |
|
2686 |
(set-window-start (selected-window) (cdr helm-current-position) t))))) |
|
2687 |
|
|
2688 |
|
|
2689 |
(defun helm-frame-or-window-configuration (save-or-restore) |
|
2690 |
"Save or restore last frame or window configuration. |
|
2691 |
Argument SAVE-OR-RESTORE is either save or restore of window or |
|
2692 |
frame configuration as per `helm-save-configuration-functions'." |
|
2693 |
(helm-log "helm-save-configuration-functions = %S" |
|
2694 |
helm-save-configuration-functions) |
|
2695 |
(let ((window-persistent-parameters (append '((no-other-window . t)) |
|
2696 |
window-persistent-parameters))) |
|
2697 |
(cl-case save-or-restore |
|
2698 |
(save (setq helm-last-frame-or-window-configuration |
|
2699 |
(funcall (cdr helm-save-configuration-functions)))) |
|
2700 |
(restore (funcall (car helm-save-configuration-functions) |
|
2701 |
helm-last-frame-or-window-configuration) |
|
2702 |
;; Restore frame focus. |
|
2703 |
;; This is needed for minibuffer own-frame config |
|
2704 |
;; when recursive minibuffers are in use. |
|
2705 |
;; e.g M-: + helm-minibuffer-history. |
|
2706 |
(cl-letf ((frame (if (minibufferp helm-current-buffer) |
|
2707 |
(selected-frame) |
|
2708 |
(last-nonminibuffer-frame))) |
|
2709 |
;; This is a workaround, because the i3 window |
|
2710 |
;; manager developers are refusing to fix their |
|
2711 |
;; broken timestamp and event handling. |
|
2712 |
;; |
|
2713 |
;; We basically just disable the part of |
|
2714 |
;; select-frame-set-input-focus that would call |
|
2715 |
;; XSetInputFocus in Xlib (x-focus-frame), that |
|
2716 |
;; resets a timestamp in the xserver which the i3 |
|
2717 |
;; developers fail to notice. |
|
2718 |
;; |
|
2719 |
;; Since they don't know about the new timestamp, |
|
2720 |
;; their keyboard handling can break after a helm |
|
2721 |
;; user quits emacs, as reported in #1641. |
|
2722 |
;; |
|
2723 |
;; Fortunately for us, we really don't need this |
|
2724 |
;; XSetInputFocus call, since we already have focus |
|
2725 |
;; for Emacs, the user is just using helm! We call |
|
2726 |
;; select-frame-set-input-focus for the other |
|
2727 |
;; side-effects, not for x-focus-frame. |
|
2728 |
((symbol-function 'x-focus-frame) #'ignore)) |
|
2729 |
(select-frame-set-input-focus frame)))))) |
|
2730 |
|
|
2731 |
(defun helm-split-window-default-fn (window) |
|
2732 |
"Default function to split windows before displaying `helm-buffer'. |
|
2733 |
|
|
2734 |
It is used as default value for |
|
2735 |
`helm-split-window-preferred-function' which is then the let-bounded |
|
2736 |
value of `split-window-preferred-function' in `helm-display-buffer'. |
|
2737 |
When `helm-display-function' which default to |
|
2738 |
`helm-default-display-buffer' is called from `helm-display-buffer' the |
|
2739 |
value of `split-window-preferred-function' will be used by `display-buffer'." |
|
2740 |
(let (split-width-threshold) |
|
2741 |
(if (and (fboundp 'window-in-direction) |
|
2742 |
;; Don't try to split when starting in a minibuffer |
|
2743 |
;; e.g M-: and try to use helm-show-kill-ring. |
|
2744 |
(not (minibufferp helm-current-buffer))) |
|
2745 |
(if (or (one-window-p t) |
|
2746 |
helm-split-window-inside-p) |
|
2747 |
(split-window |
|
2748 |
(selected-window) nil (if (eq helm-split-window-default-side 'other) |
|
2749 |
'below helm-split-window-default-side)) |
|
2750 |
;; If more than one window reuse one of them. |
|
2751 |
(cl-case helm-split-window-default-side |
|
2752 |
(left (or (helm-window-in-direction 'left) |
|
2753 |
(helm-window-in-direction 'above) |
|
2754 |
(selected-window))) |
|
2755 |
(above (or (helm-window-in-direction 'above) |
|
2756 |
(helm-window-in-direction 'left) |
|
2757 |
(selected-window))) |
|
2758 |
(right (or (helm-window-in-direction 'right) |
|
2759 |
(helm-window-in-direction 'below) |
|
2760 |
(selected-window))) |
|
2761 |
(below (or (helm-window-in-direction 'below) |
|
2762 |
(helm-window-in-direction 'right) |
|
2763 |
(selected-window))) |
|
2764 |
(same (selected-window)) |
|
2765 |
(other (other-window-for-scrolling)) |
|
2766 |
(t (or (window-next-sibling) (selected-window))))) |
|
2767 |
(split-window-sensibly window)))) |
|
2768 |
|
|
2769 |
(defun helm-window-in-direction (direction) |
|
2770 |
"Same as `window-in-direction' but check if window is dedicated." |
|
2771 |
(helm-aif (window-in-direction direction) |
|
2772 |
(and (not (window-dedicated-p it)) it))) |
|
2773 |
|
|
2774 |
|
|
2775 |
;;; Display helm buffer |
|
2776 |
;; |
|
2777 |
;; |
|
2778 |
(defun helm-resolve-display-function (com) |
|
2779 |
"Decide which display function use according to `helm-commands-using-frame'. |
|
2780 |
|
|
2781 |
The `helm-display-function' buffer local value takes precedence on |
|
2782 |
`helm-commands-using-frame'. |
|
2783 |
If `helm-initial-frame' has no minibuffer, use |
|
2784 |
`helm-display-buffer-in-own-frame' function. |
|
2785 |
Fallback to global value of `helm-display-function' when no local |
|
2786 |
value found and current command is not in `helm-commands-using-frame'." |
|
2787 |
(or (with-helm-buffer helm-display-function) |
|
2788 |
(and (or (memq com helm-commands-using-frame) |
|
2789 |
(and helm-use-frame-when-more-than-two-windows |
|
2790 |
(null helm--nested) |
|
2791 |
(> (length (window-list)) 2)) |
|
2792 |
;; Frame parameter is unreliable for minibuffer on emacs-26. |
|
2793 |
(null (member helm-initial-frame (minibuffer-frame-list)))) |
|
2794 |
#'helm-display-buffer-in-own-frame) |
|
2795 |
(default-value 'helm-display-function))) |
|
2796 |
|
|
2797 |
(defun helm-display-buffer (buffer &optional resume) |
|
2798 |
"Display BUFFER. |
|
2799 |
|
|
2800 |
The function used to display `helm-buffer' by calling |
|
2801 |
`helm-display-function' which split window with |
|
2802 |
`helm-split-window-preferred-function'." |
|
2803 |
(let ((split-window-preferred-function |
|
2804 |
helm-split-window-preferred-function) |
|
2805 |
(helm-split-window-default-side |
|
2806 |
(if (and (not helm-full-frame) |
|
2807 |
helm-reuse-last-window-split-state) |
|
2808 |
(cond ((eq helm-split-window-default-side 'same) 'same) |
|
2809 |
((eq helm-split-window-default-side 'other) 'other) |
|
2810 |
(helm--window-side-state) |
|
2811 |
(t helm-split-window-default-side)) |
|
2812 |
helm-split-window-default-side)) |
|
2813 |
(disp-fn (with-current-buffer buffer |
|
2814 |
(helm-resolve-display-function |
|
2815 |
(if helm-actions-inherit-frame-settings |
|
2816 |
(helm-this-command) this-command))))) |
|
2817 |
(prog1 |
|
2818 |
(funcall disp-fn buffer (or (helm-resume-p resume) |
|
2819 |
(and helm-actions-inherit-frame-settings |
|
2820 |
helm--executing-helm-action))) |
|
2821 |
(with-helm-buffer (setq-local helm-display-function disp-fn)) |
|
2822 |
(setq helm-onewindow-p (one-window-p t)) |
|
2823 |
;; Don't allow other-window and friends switching out of minibuffer. |
|
2824 |
(when helm-prevent-escaping-from-minibuffer |
|
2825 |
(helm-prevent-switching-other-window))))) |
|
2826 |
|
|
2827 |
(cl-defun helm-prevent-switching-other-window (&key (enabled t)) |
|
2828 |
"Allow setting `no-other-window' parameter for all windows. |
|
2829 |
Arg ENABLE is the value of `no-other-window' window property." |
|
2830 |
(walk-windows |
|
2831 |
(lambda (w) |
|
2832 |
(unless (window-dedicated-p w) |
|
2833 |
(set-window-parameter w 'no-other-window enabled))) |
|
2834 |
0)) |
|
2835 |
|
|
2836 |
(defun helm-default-display-buffer (buffer &optional _resume) |
|
2837 |
"Default function to display `helm-buffer' BUFFER. |
|
2838 |
|
|
2839 |
It is the default value of `helm-display-function' |
|
2840 |
It uses `switch-to-buffer' or `display-buffer' depending on the |
|
2841 |
value of `helm-full-frame' or `helm-split-window-default-side'." |
|
2842 |
(let (pop-up-frames) |
|
2843 |
(if (or (buffer-local-value 'helm-full-frame (get-buffer buffer)) |
|
2844 |
(and (eq helm-split-window-default-side 'same) |
|
2845 |
(one-window-p t))) |
|
2846 |
(progn (and (not (minibufferp helm-current-buffer)) |
|
2847 |
(delete-other-windows)) |
|
2848 |
(switch-to-buffer buffer)) |
|
2849 |
(when (and (or helm-always-two-windows helm-autoresize-mode) |
|
2850 |
(not (eq helm-split-window-default-side 'same)) |
|
2851 |
(not (minibufferp helm-current-buffer)) |
|
2852 |
(not helm-split-window-inside-p)) |
|
2853 |
(delete-other-windows)) |
|
2854 |
(display-buffer |
|
2855 |
buffer `(,helm-default-display-buffer-functions |
|
2856 |
. ,(append helm-default-display-buffer-alist |
|
2857 |
`((window-height . ,helm-display-buffer-default-height) |
|
2858 |
(window-width . ,helm-display-buffer-default-width))))) |
|
2859 |
(helm-log-run-hook 'helm-window-configuration-hook)))) |
|
2860 |
|
|
2861 |
(defun helm-display-buffer-in-own-frame (buffer &optional resume) |
|
2862 |
"Display helm buffer BUFFER in a separate frame. |
|
2863 |
|
|
2864 |
Function suitable for `helm-display-function', |
|
2865 |
`helm-completion-in-region-display-function' |
|
2866 |
and/or `helm-show-completion-default-display-function'. |
|
2867 |
|
|
2868 |
See `helm-display-buffer-height' and `helm-display-buffer-width' to |
|
2869 |
configure frame size. |
|
2870 |
|
|
2871 |
Note that this feature is available only with emacs-25+." |
|
2872 |
(cl-assert (and (fboundp 'window-absolute-pixel-edges) |
|
2873 |
(fboundp 'frame-geometry)) |
|
2874 |
nil "Helm buffer in own frame is only available starting at emacs-25+") |
|
2875 |
(if (not (display-graphic-p)) |
|
2876 |
;; Fallback to default when frames are not usable. |
|
2877 |
(helm-default-display-buffer buffer) |
|
2878 |
(setq helm--buffer-in-new-frame-p t) |
|
2879 |
(let* ((pos (window-absolute-pixel-position)) |
|
2880 |
(half-screen-size (/ (display-pixel-height x-display-name) 2)) |
|
2881 |
(frame-info (frame-geometry)) |
|
2882 |
(prmt-size (length helm--prompt)) |
|
2883 |
(line-height (frame-char-height)) |
|
2884 |
(default-frame-alist |
|
2885 |
(if resume |
|
2886 |
(buffer-local-value 'helm--last-frame-parameters |
|
2887 |
(get-buffer buffer)) |
|
2888 |
`((width . ,helm-display-buffer-width) |
|
2889 |
(height . ,helm-display-buffer-height) |
|
2890 |
(tool-bar-lines . 0) |
|
2891 |
(left . ,(- (car pos) |
|
2892 |
(* (frame-char-width) |
|
2893 |
(if (< (- (point) (point-at-bol)) prmt-size) |
|
2894 |
(- (point) (point-at-bol)) |
|
2895 |
prmt-size)))) |
|
2896 |
;; Try to put frame at the best possible place. |
|
2897 |
;; Frame should be below point if enough |
|
2898 |
;; place, otherwise above point and |
|
2899 |
;; current line should not be hidden |
|
2900 |
;; by helm frame. |
|
2901 |
(top . ,(if (> (cdr pos) half-screen-size) |
|
2902 |
;; Above point |
|
2903 |
(- (cdr pos) |
|
2904 |
;; add 2 lines to make sure there is always a gap |
|
2905 |
(* (+ helm-display-buffer-height 2) line-height) |
|
2906 |
;; account for title bar height too |
|
2907 |
(cddr (assq 'title-bar-size frame-info))) |
|
2908 |
;; Below point |
|
2909 |
(+ (cdr pos) line-height))) |
|
2910 |
(title . "Helm") |
|
2911 |
(undecorated . ,helm-use-undecorated-frame-option) |
|
2912 |
(vertical-scroll-bars . nil) |
|
2913 |
(menu-bar-lines . 0) |
|
2914 |
(fullscreen . nil) |
|
2915 |
(visibility . ,(null helm-display-buffer-reuse-frame)) |
|
2916 |
(minibuffer . t)))) |
|
2917 |
display-buffer-alist) |
|
2918 |
;; Display minibuffer above or below only in initial session, |
|
2919 |
;; not on a session triggered by action, this way if user have |
|
2920 |
;; toggled minibuffer and header-line manually she keeps this |
|
2921 |
;; setting in next action. |
|
2922 |
(unless (or helm--executing-helm-action resume) |
|
2923 |
;; Add the hook inconditionally, if |
|
2924 |
;; helm-echo-input-in-header-line is nil helm-hide-minibuffer-maybe |
|
2925 |
;; will have anyway no effect so no need to remove the hook. |
|
2926 |
(add-hook 'helm-minibuffer-set-up-hook 'helm-hide-minibuffer-maybe) |
|
2927 |
(with-helm-buffer |
|
2928 |
(setq-local helm-echo-input-in-header-line |
|
2929 |
(not (> (cdr pos) half-screen-size))))) |
|
2930 |
(helm-display-buffer-popup-frame buffer default-frame-alist) |
|
2931 |
;; When frame size have been modified manually by user restore |
|
2932 |
;; it to default value unless resuming or not using |
|
2933 |
;; `helm-display-buffer-reuse-frame'. |
|
2934 |
;; This have to be done AFTER raising the frame otherwise |
|
2935 |
;; minibuffer visibility is lost until next session. |
|
2936 |
(unless (or resume (not helm-display-buffer-reuse-frame)) |
|
2937 |
(set-frame-size helm-popup-frame |
|
2938 |
helm-display-buffer-width |
|
2939 |
helm-display-buffer-height))) |
|
2940 |
(helm-log-run-hook 'helm-window-configuration-hook))) |
|
2941 |
|
|
2942 |
(defun helm-display-buffer-popup-frame (buffer frame-alist) |
|
2943 |
(if helm-display-buffer-reuse-frame |
|
2944 |
(let* ((x (cdr (assoc 'left frame-alist))) |
|
2945 |
(y (cdr (assoc 'top frame-alist)))) |
|
2946 |
(unless (and helm-popup-frame |
|
2947 |
(frame-live-p helm-popup-frame)) |
|
2948 |
(setq helm-popup-frame (make-frame frame-alist))) |
|
2949 |
(select-frame helm-popup-frame) |
|
2950 |
(set-frame-position helm-popup-frame x y) |
|
2951 |
(switch-to-buffer buffer) |
|
2952 |
(select-frame-set-input-focus helm-popup-frame t)) |
|
2953 |
;; If user have changed `helm-display-buffer-reuse-frame' to nil |
|
2954 |
;; maybe kill the frame. |
|
2955 |
(when (and helm-popup-frame |
|
2956 |
(frame-live-p helm-popup-frame)) |
|
2957 |
(delete-frame helm-popup-frame)) |
|
2958 |
(display-buffer |
|
2959 |
buffer '(display-buffer-pop-up-frame . nil)))) |
|
2960 |
|
|
2961 |
;; Ensure to quit helm when user delete helm frame manually. |
|
2962 |
;; If user deletes another frame keep session running. |
|
2963 |
(defun helm--delete-frame-function (frame) |
|
2964 |
(when (and helm-alive-p |
|
2965 |
;; FRAME is handling helm-buffer |
|
2966 |
(get-buffer-window helm-buffer frame)) |
|
2967 |
(helm-keyboard-quit))) |
|
2968 |
(add-hook 'delete-frame-functions 'helm--delete-frame-function) |
|
2969 |
|
|
2970 |
;;; Initialize |
|
2971 |
;; |
|
2972 |
(defun helm-get-sources (sources) |
|
2973 |
"Transform each element of SOURCES in alist. |
|
2974 |
Returns the resulting list." |
|
2975 |
(when sources |
|
2976 |
(mapcar (lambda (source) |
|
2977 |
(if (listp source) |
|
2978 |
source (symbol-value source))) |
|
2979 |
(helm-normalize-sources sources)))) |
|
2980 |
|
|
2981 |
(defun helm-initialize (any-resume any-input any-default any-sources) |
|
2982 |
"Start initialization of `helm' session. |
|
2983 |
For ANY-RESUME ANY-INPUT ANY-DEFAULT and ANY-SOURCES See `helm'." |
|
2984 |
(helm-log "start initialization: any-resume=%S any-input=%S" |
|
2985 |
any-resume any-input) |
|
2986 |
(helm-frame-or-window-configuration 'save) |
|
2987 |
(let ((sources (helm-get-sources any-sources))) |
|
2988 |
(setq helm--in-fuzzy |
|
2989 |
(cl-loop for s in sources |
|
2990 |
for matchfns = (helm-match-functions s) |
|
2991 |
for searchfns = (helm-search-functions s) |
|
2992 |
when (or (memq 'helm-fuzzy-match matchfns) |
|
2993 |
(memq 'helm-fuzzy-search searchfns)) |
|
2994 |
return t)) |
|
2995 |
(helm-log "sources = %S" sources) |
|
2996 |
(helm-set-local-variable 'helm-sources sources) |
|
2997 |
;; Once `helm-buffer' is created `helm-sources' will be a local |
|
2998 |
;; variable which value is a list of alists. |
|
2999 |
(helm-current-position 'save) |
|
3000 |
(if (helm-resume-p any-resume) |
|
3001 |
(helm-initialize-overlays (helm-buffer-get)) |
|
3002 |
(helm-initial-setup any-default sources)) |
|
3003 |
(setq helm-alive-p t) |
|
3004 |
(unless (eq any-resume 'noresume) |
|
3005 |
(helm--push-and-remove-dups helm-buffer 'helm-buffers) |
|
3006 |
(setq helm-last-buffer helm-buffer)) |
|
3007 |
(when any-input |
|
3008 |
(setq helm-input any-input |
|
3009 |
helm-pattern any-input) |
|
3010 |
(helm--fuzzy-match-maybe-set-pattern)) |
|
3011 |
;; If a `resume' attribute is present `helm-compute-attr-in-sources' |
|
3012 |
;; will run its function. |
|
3013 |
(when (helm-resume-p any-resume) |
|
3014 |
(helm-compute-attr-in-sources 'resume)) |
|
3015 |
(helm-log "end initialization"))) |
|
3016 |
|
|
3017 |
(defun helm-initialize-overlays (buffer) |
|
3018 |
"Initialize helm overlays in BUFFER." |
|
3019 |
(helm-log "overlay setup") |
|
3020 |
(if helm-selection-overlay |
|
3021 |
;; make sure the overlay belongs to the helm buffer if |
|
3022 |
;; it's newly created |
|
3023 |
(move-overlay helm-selection-overlay (point-min) (point-min) |
|
3024 |
(get-buffer buffer)) |
|
3025 |
|
|
3026 |
(setq helm-selection-overlay |
|
3027 |
(make-overlay (point-min) (point-min) (get-buffer buffer))) |
|
3028 |
(overlay-put helm-selection-overlay 'face 'helm-selection) |
|
3029 |
(overlay-put helm-selection-overlay 'priority 1))) |
|
3030 |
|
|
3031 |
(defun helm-restore-position-on-quit () |
|
3032 |
"Restore position in `helm-current-buffer' when quitting." |
|
3033 |
(helm-current-position 'restore)) |
|
3034 |
|
|
3035 |
(defun helm--push-and-remove-dups (elm sym) |
|
3036 |
"Move ELM of SYM value on top and set SYM to this new value." |
|
3037 |
(set sym (cons elm (delete elm (symbol-value sym))))) |
|
3038 |
|
|
3039 |
(defun helm--current-buffer () |
|
3040 |
"[internal] Return `current-buffer' BEFORE `helm-buffer' is initialized. |
|
3041 |
Note that it returns the minibuffer in use after helm has started |
|
3042 |
and is intended for `helm-initial-setup'. To get the buffer where |
|
3043 |
helm was started, use `helm-current-buffer' instead." |
|
3044 |
(if (minibuffer-window-active-p (minibuffer-window)) |
|
3045 |
;; If minibuffer is active be sure to use it's buffer |
|
3046 |
;; as `helm-current-buffer', this allow to use helm |
|
3047 |
;; from an already active minibuffer (M-: etc...) |
|
3048 |
(window-buffer (active-minibuffer-window)) |
|
3049 |
;; Fix Issue #456 |
|
3050 |
;; Use this instead of `current-buffer' to ensure |
|
3051 |
;; helm session started in helm-mode from a completing-read |
|
3052 |
;; Use really the buffer where we started and not the one |
|
3053 |
;; where the completing-read is wrapped. i.e |
|
3054 |
;; (with-current-buffer SOME-OTHER-BUFFER (completing-read [...]) |
|
3055 |
(window-buffer (with-selected-window (minibuffer-window) |
|
3056 |
(minibuffer-selected-window))))) |
|
3057 |
|
|
3058 |
(defun helm--run-init-hooks (hook sources) |
|
3059 |
"Run after and before init hooks local to source. |
|
3060 |
See :after-init-hook and :before-init-hook in `helm-source'." |
|
3061 |
(cl-loop with sname = (cl-ecase hook |
|
3062 |
(before-init-hook "h-before-init-hook") |
|
3063 |
(after-init-hook "h-after-init-hook")) |
|
3064 |
with h = (cl-gensym sname) |
|
3065 |
for s in sources |
|
3066 |
for hv = (assoc-default hook s) |
|
3067 |
if (and hv (not (symbolp hv))) |
|
3068 |
do (set h hv) |
|
3069 |
and do (helm-log-run-hook h) |
|
3070 |
else do (helm-log-run-hook hv))) |
|
3071 |
|
|
3072 |
(defun helm-initial-setup (any-default sources) |
|
3073 |
"Initialize helm settings and set up the helm buffer." |
|
3074 |
;; Run global hook. |
|
3075 |
(helm-log-run-hook 'helm-before-initialize-hook) |
|
3076 |
;; Run local source hook. |
|
3077 |
(helm--run-init-hooks 'before-init-hook sources) |
|
3078 |
;; For initialization of helm locals vars that need |
|
3079 |
;; a value from current buffer, it is here. |
|
3080 |
(helm-set-local-variable 'current-input-method current-input-method) |
|
3081 |
(setq helm-current-prefix-arg nil |
|
3082 |
helm-saved-action nil |
|
3083 |
helm-saved-selection nil |
|
3084 |
helm-suspend-update-flag nil |
|
3085 |
;; Ensure this is called BEFORE selecting helm-window. |
|
3086 |
helm-current-buffer (helm--current-buffer) |
|
3087 |
helm-buffer-file-name buffer-file-name |
|
3088 |
helm-issued-errors nil |
|
3089 |
helm-saved-current-source nil |
|
3090 |
helm--suspend-update-interactive-flag nil) |
|
3091 |
(when (and (with-helm-current-buffer |
|
3092 |
(and (buffer-narrowed-p) |
|
3093 |
(use-region-p))) |
|
3094 |
(not helm--nested)) |
|
3095 |
(helm-set-local-variable 'helm--current-buffer-narrowed |
|
3096 |
(list (current-buffer) |
|
3097 |
(region-beginning) (region-end)))) |
|
3098 |
(unless (and (or helm-split-window-state |
|
3099 |
helm--window-side-state) |
|
3100 |
helm-reuse-last-window-split-state) |
|
3101 |
(setq helm-split-window-state |
|
3102 |
(if (or (null split-width-threshold) |
|
3103 |
(and (integerp split-width-threshold) |
|
3104 |
(>= split-width-threshold (+ (frame-width) 4)))) |
|
3105 |
'vertical 'horizontal)) |
|
3106 |
(setq helm--window-side-state |
|
3107 |
(or helm-split-window-default-side 'below))) |
|
3108 |
;; Call the init function for sources where appropriate |
|
3109 |
(helm-compute-attr-in-sources 'init sources) |
|
3110 |
(setq helm-pattern (or (and helm--maybe-use-default-as-input |
|
3111 |
(or (if (listp any-default) |
|
3112 |
(car any-default) any-default) |
|
3113 |
(with-helm-current-buffer |
|
3114 |
(thing-at-point 'symbol)))) |
|
3115 |
"")) |
|
3116 |
(setq helm-input "") |
|
3117 |
(clrhash helm-candidate-cache) |
|
3118 |
(helm-create-helm-buffer) |
|
3119 |
(helm-clear-visible-mark) |
|
3120 |
;; Run global hook. |
|
3121 |
(helm-log-run-hook 'helm-after-initialize-hook) |
|
3122 |
;; Run local source hook. |
|
3123 |
(helm--run-init-hooks 'after-init-hook sources)) |
|
3124 |
|
|
3125 |
(define-derived-mode helm-major-mode |
|
3126 |
fundamental-mode "Hmm" |
|
3127 |
"[Internal] Provide major-mode name in helm buffers. |
|
3128 |
Unuseful when used outside helm, don't use it.") |
|
3129 |
(put 'helm-major-mode 'mode-class 'special) |
|
3130 |
(put 'helm-major-mode 'helm-only t) |
|
3131 |
|
|
3132 |
(defun helm-create-helm-buffer () |
|
3133 |
"Create and setup `helm-buffer'." |
|
3134 |
(let ((root-dir default-directory) |
|
3135 |
(inhibit-read-only t)) |
|
3136 |
(with-current-buffer (get-buffer-create helm-buffer) |
|
3137 |
(helm-log "Enabling major-mode %S" major-mode) |
|
3138 |
(helm-log "kill local variables: %S" (buffer-local-variables)) |
|
3139 |
(kill-all-local-variables) |
|
3140 |
(helm-major-mode) |
|
3141 |
(set (make-local-variable 'buffer-read-only) nil) |
|
3142 |
(buffer-disable-undo) |
|
3143 |
(erase-buffer) |
|
3144 |
(set (make-local-variable 'helm-map) helm-map) |
|
3145 |
(set (make-local-variable 'helm-source-filter) nil) |
|
3146 |
(make-local-variable 'helm-sources) |
|
3147 |
(set (make-local-variable 'helm-display-function) nil) |
|
3148 |
(set (make-local-variable 'helm-selection-point) nil) |
|
3149 |
(set (make-local-variable 'scroll-margin) |
|
3150 |
(if helm-display-source-at-screen-top |
|
3151 |
0 helm-completion-window-scroll-margin)) |
|
3152 |
(set (make-local-variable 'default-directory) root-dir) |
|
3153 |
(set (make-local-variable 'helm-marked-candidates) nil) |
|
3154 |
(set (make-local-variable 'helm--prompt) helm--prompt) |
|
3155 |
(helm-initialize-persistent-action) |
|
3156 |
(helm-log "helm-display-function = %S" helm-display-function) |
|
3157 |
(helm-log "helm--local-variables = %S" helm--local-variables) |
|
3158 |
(helm--set-local-variables-internal) |
|
3159 |
(setq truncate-lines helm-truncate-lines) ; already local. |
|
3160 |
(setq cursor-type nil)) |
|
3161 |
(helm-initialize-overlays helm-buffer) |
|
3162 |
(get-buffer helm-buffer))) |
|
3163 |
|
|
3164 |
(define-minor-mode helm--minor-mode |
|
3165 |
"[INTERNAL] Enable keymap in helm minibuffer. |
|
3166 |
Since this mode has no effect when run outside of helm context, |
|
3167 |
please don't use it outside helm. |
|
3168 |
|
|
3169 |
\\{helm-map}" |
|
3170 |
:group 'helm |
|
3171 |
:keymap (and helm-alive-p helm-map) |
|
3172 |
(unless helm-alive-p (setq helm--minor-mode nil))) |
|
3173 |
(put 'helm--minor-mode 'helm-only t) |
|
3174 |
|
|
3175 |
(defun helm--reset-default-pattern () |
|
3176 |
(setq helm-pattern "") |
|
3177 |
(setq helm--maybe-use-default-as-input nil)) |
|
3178 |
|
|
3179 |
(defun helm-read-pattern-maybe (any-prompt any-input |
|
3180 |
any-preselect any-resume any-keymap |
|
3181 |
any-default any-history) |
|
3182 |
"Read pattern with prompt ANY-PROMPT and initial input ANY-INPUT. |
|
3183 |
For ANY-PRESELECT ANY-RESUME ANY-KEYMAP ANY-DEFAULT ANY-HISTORY, See `helm'." |
|
3184 |
(with-helm-buffer |
|
3185 |
(if (and (helm-resume-p any-resume) |
|
3186 |
;; When no source, helm-buffer is empty |
|
3187 |
;; or contain non--candidate lines (e.g grep exit status) |
|
3188 |
(helm-get-current-source)) |
|
3189 |
(helm-mark-current-line t) |
|
3190 |
(helm-update any-preselect)) |
|
3191 |
(let* ((src (helm-get-current-source)) |
|
3192 |
(src-keymap (assoc-default 'keymap src)) |
|
3193 |
(hist (or (and any-history (symbolp any-history) any-history) |
|
3194 |
;; Needed for resuming. |
|
3195 |
(assoc-default 'history src))) |
|
3196 |
(timer nil) |
|
3197 |
blink-matching-paren |
|
3198 |
(resize-mini-windows (and (null helm-echo-input-in-header-line) |
|
3199 |
resize-mini-windows)) |
|
3200 |
(first-src (car helm-sources)) |
|
3201 |
(source-process-p (or (assq 'candidates-process src) |
|
3202 |
(assq 'candidates-process first-src)))) |
|
3203 |
(helm-log "helm-get-candidate-number => %S" |
|
3204 |
(helm-get-candidate-number)) |
|
3205 |
(helm-log "helm-execute-action-at-once-if-one = %S" |
|
3206 |
helm-execute-action-at-once-if-one) |
|
3207 |
(helm-log "helm-quit-if-no-candidate = %S" helm-quit-if-no-candidate) |
|
3208 |
(when (and src (helm-resume-p any-resume)) |
|
3209 |
(helm-display-mode-line src)) |
|
3210 |
;; Reset `helm-pattern' and update |
|
3211 |
;; display if no result found with precedent value of `helm-pattern' |
|
3212 |
;; unless `helm-quit-if-no-candidate' is non-`nil', in this case |
|
3213 |
;; Don't force update with an empty pattern. |
|
3214 |
;; Reset also `helm--maybe-use-default-as-input' as this checking |
|
3215 |
;; happen only on startup. |
|
3216 |
(when helm--maybe-use-default-as-input |
|
3217 |
;; Store value of `default' temporarily here waiting next update |
|
3218 |
;; to allow actions like helm-moccur-action matching pattern |
|
3219 |
;; at the place it jump to. |
|
3220 |
(setq helm-input helm-pattern) |
|
3221 |
(if source-process-p |
|
3222 |
;; Reset pattern to next update. |
|
3223 |
(with-helm-after-update-hook |
|
3224 |
(helm--reset-default-pattern)) |
|
3225 |
;; Reset pattern right now. |
|
3226 |
(helm--reset-default-pattern)) |
|
3227 |
;; Ensure force-update when no candidates |
|
3228 |
;; when we start with an empty pattern. |
|
3229 |
(and (helm-empty-buffer-p) |
|
3230 |
(null helm-quit-if-no-candidate) |
|
3231 |
(helm-force-update))) |
|
3232 |
;; Handle `helm-execute-action-at-once-if-one' and |
|
3233 |
;; `helm-quit-if-no-candidate' now. |
|
3234 |
(cond ((and (if (functionp helm-execute-action-at-once-if-one) |
|
3235 |
(funcall helm-execute-action-at-once-if-one) |
|
3236 |
helm-execute-action-at-once-if-one) |
|
3237 |
(= (helm-get-candidate-number |
|
3238 |
(eq helm-execute-action-at-once-if-one 'current-source)) |
|
3239 |
1)) |
|
3240 |
(ignore)) ; Don't enter the minibuffer loop. |
|
3241 |
((and helm-quit-if-no-candidate |
|
3242 |
(= (helm-get-candidate-number) 0)) |
|
3243 |
(setq helm-quit t) |
|
3244 |
(and (functionp helm-quit-if-no-candidate) |
|
3245 |
(funcall helm-quit-if-no-candidate))) |
|
3246 |
(t ; Enter now minibuffer and wait for input. |
|
3247 |
(let ((tap (or any-default |
|
3248 |
(with-helm-current-buffer |
|
3249 |
(thing-at-point 'symbol))))) |
|
3250 |
(when helm-execute-action-at-once-if-one |
|
3251 |
(helm-display-buffer helm-buffer any-resume) |
|
3252 |
(select-window (helm-window))) |
|
3253 |
(unwind-protect |
|
3254 |
(minibuffer-with-setup-hook |
|
3255 |
(lambda () |
|
3256 |
;; Start minor-mode with global value of helm-map. |
|
3257 |
(helm--minor-mode 1) |
|
3258 |
;; Now override the global value of `helm-map' with |
|
3259 |
;; the local one which is in this order: |
|
3260 |
;; - The keymap of current source. |
|
3261 |
;; - The value passed in ANY-KEYMAP |
|
3262 |
;; - Or fallback to the global value of helm-map. |
|
3263 |
(helm--maybe-update-keymap |
|
3264 |
(or src-keymap any-keymap helm-map)) |
|
3265 |
(helm-log-run-hook 'helm-minibuffer-set-up-hook) |
|
3266 |
(setq timer |
|
3267 |
(run-with-idle-timer |
|
3268 |
(max (with-helm-buffer helm-input-idle-delay) |
|
3269 |
0.001) |
|
3270 |
'repeat |
|
3271 |
(lambda () |
|
3272 |
;; Stop updating in persistent action |
|
3273 |
;; or when `helm-suspend-update-flag' |
|
3274 |
;; is non-`nil'. |
|
3275 |
(unless (or helm-in-persistent-action |
|
3276 |
helm-suspend-update-flag) |
|
3277 |
(save-selected-window |
|
3278 |
(helm-check-minibuffer-input) |
|
3279 |
(helm-print-error-messages)))))) |
|
3280 |
;; minibuffer has already been filled here. |
|
3281 |
(helm--update-header-line)) |
|
3282 |
(read-from-minibuffer (propertize (or any-prompt "pattern: ") |
|
3283 |
'face 'helm-minibuffer-prompt) |
|
3284 |
any-input helm-map |
|
3285 |
nil hist tap |
|
3286 |
helm-inherit-input-method)) |
|
3287 |
(when timer (cancel-timer timer) (setq timer nil))))))))) |
|
3288 |
|
|
3289 |
(defun helm-toggle-suspend-update () |
|
3290 |
"Enable or disable display update in helm. |
|
3291 |
This can be useful for example for quietly writing a complex regexp |
|
3292 |
without helm constantly updating." |
|
3293 |
(interactive) |
|
3294 |
(helm-suspend-update (not helm-suspend-update-flag) t) |
|
3295 |
(setq helm--suspend-update-interactive-flag |
|
3296 |
(not helm--suspend-update-interactive-flag))) |
|
3297 |
(put 'helm-toggle-suspend-update 'helm-only t) |
|
3298 |
|
|
3299 |
(defun helm-suspend-update (arg &optional verbose) |
|
3300 |
"Enable or disable display update in helm. |
|
3301 |
If ARG is 1 or non nil suspend update, if it is -1 or nil reenable |
|
3302 |
updating. When VERBOSE is specified display a message." |
|
3303 |
(with-helm-buffer |
|
3304 |
(when (setq helm-suspend-update-flag |
|
3305 |
(helm-acase arg |
|
3306 |
(1 t) |
|
3307 |
(-1 nil) |
|
3308 |
(t it))) |
|
3309 |
(helm-kill-async-processes) |
|
3310 |
(setq helm-pattern "")) |
|
3311 |
(when verbose |
|
3312 |
(message (if helm-suspend-update-flag |
|
3313 |
"Helm update suspended!" |
|
3314 |
"Helm update re-enabled!"))) |
|
3315 |
(helm-aif (helm-get-current-source) |
|
3316 |
(helm-display-mode-line it t)))) |
|
3317 |
|
|
3318 |
(defun helm-delete-backward-no-update (arg) |
|
3319 |
"Disable update and delete ARG chars backward. |
|
3320 |
Update is reenabled when idle 1s." |
|
3321 |
(interactive "p") |
|
3322 |
(with-helm-alive-p |
|
3323 |
(unless helm--suspend-update-interactive-flag |
|
3324 |
(helm-suspend-update 1)) |
|
3325 |
(backward-delete-char arg) |
|
3326 |
(run-with-idle-timer |
|
3327 |
1 nil |
|
3328 |
(lambda () |
|
3329 |
(unless helm--suspend-update-interactive-flag |
|
3330 |
(helm-suspend-update -1) |
|
3331 |
(helm-check-minibuffer-input) |
|
3332 |
(helm-force-update)))))) |
|
3333 |
(put 'helm-delete-backward-no-update 'helm-only t) |
|
3334 |
|
|
3335 |
(defun helm--suspend-read-passwd (old--fn &rest args) |
|
3336 |
"Suspend helm while reading password. |
|
3337 |
This is used to advice `tramp-read-passwd', `ange-ftp-get-passwd' and |
|
3338 |
`epa-passphrase-callback-function'." |
|
3339 |
;; Suspend update when prompting for a tramp password. |
|
3340 |
(setq helm-suspend-update-flag t) |
|
3341 |
(setq overriding-terminal-local-map nil) |
|
3342 |
(setq helm--reading-passwd-or-string t) |
|
3343 |
(unwind-protect |
|
3344 |
;; No need to suspend timer in emacs-24.4 |
|
3345 |
;; it is fixed upstream. |
|
3346 |
(apply old--fn args) |
|
3347 |
(setq helm--reading-passwd-or-string nil) |
|
3348 |
(setq helm-suspend-update-flag nil))) |
|
3349 |
|
|
3350 |
(defun helm--maybe-update-keymap (&optional map) |
|
3351 |
"Handle different keymaps in multiples sources. |
|
3352 |
|
|
3353 |
Overrides `helm-map' with the local map of current source. If no |
|
3354 |
map is found in current source, does nothing (keeps previous |
|
3355 |
map)." |
|
3356 |
(with-helm-buffer |
|
3357 |
(helm-aif (or map (assoc-default 'keymap (helm-get-current-source))) |
|
3358 |
;; We used a timer in the past to leave |
|
3359 |
;; enough time to helm to setup its keymap |
|
3360 |
;; when changing source from a recursive minibuffer. |
|
3361 |
;; e.g C-x C-f M-y C-g |
|
3362 |
;; => *find-files have now the bindings of *kill-ring. |
|
3363 |
;; It is no more true now we are using `minor-mode-overriding-map-alist' |
|
3364 |
;; and `helm--minor-mode' thus it fix issue #1076 for emacs-24.3 |
|
3365 |
;; where concurrent timers are not supported. |
|
3366 |
;; i.e update keymap+check input. |
|
3367 |
(with-current-buffer (window-buffer (minibuffer-window)) |
|
3368 |
(setq minor-mode-overriding-map-alist `((helm--minor-mode . ,it))))))) |
|
3369 |
|
|
3370 |
;;; Prevent loosing focus when using mouse. |
|
3371 |
;; |
|
3372 |
(defvar helm--remap-mouse-mode-map |
|
3373 |
(let ((map (make-sparse-keymap))) |
|
3374 |
(cl-loop for k in '([mouse-1] [mouse-2] [mouse-3] |
|
3375 |
[down-mouse-1] [down-mouse-2] [down-mouse-3] |
|
3376 |
[drag-mouse-1] [drag-mouse-2] [drag-mouse-3] |
|
3377 |
[double-mouse-1] [double-mouse-2] [double-mouse-3] |
|
3378 |
[triple-mouse-1] [triple-mouse-2] [triple-mouse-3]) |
|
3379 |
do (define-key map k 'ignore)) |
|
3380 |
map)) |
|
3381 |
|
|
3382 |
(define-minor-mode helm--remap-mouse-mode |
|
3383 |
"[INTERNAL] Prevent escaping helm minibuffer with mouse clicks. |
|
3384 |
Do nothing when used outside of helm context. |
|
3385 |
|
|
3386 |
WARNING: Do not use this mode yourself, it is internal to helm." |
|
3387 |
:group 'helm |
|
3388 |
:global t |
|
3389 |
:keymap helm--remap-mouse-mode-map |
|
3390 |
(unless helm-alive-p |
|
3391 |
(setq helm--remap-mouse-mode-map nil))) |
|
3392 |
(put 'helm--remap-mouse-mode 'helm-only t) |
|
3393 |
|
|
3394 |
;; Clean up |
|
3395 |
|
|
3396 |
(defun helm-cleanup () |
|
3397 |
"Clean up the mess when helm exit or quit." |
|
3398 |
(helm-log "start cleanup") |
|
3399 |
(with-current-buffer helm-buffer |
|
3400 |
(let ((frame (selected-frame))) |
|
3401 |
(setq cursor-type t) |
|
3402 |
(remove-hook 'post-command-hook 'helm--maybe-update-keymap) |
|
3403 |
(remove-hook 'post-command-hook 'helm--update-header-line) |
|
3404 |
;; Be sure we call cleanup functions from helm-buffer. |
|
3405 |
(helm-compute-attr-in-sources 'cleanup) |
|
3406 |
;; Delete or make invisible helm frame. |
|
3407 |
(if (and helm--buffer-in-new-frame-p (null helm--nested)) |
|
3408 |
(progn |
|
3409 |
(setq-local helm--last-frame-parameters |
|
3410 |
(helm--get-frame-parameters)) |
|
3411 |
(bury-buffer) |
|
3412 |
(if helm-display-buffer-reuse-frame |
|
3413 |
(make-frame-invisible frame) (delete-frame frame))) |
|
3414 |
;; bury-buffer from this window [1]. |
|
3415 |
;; Do it at end to make sure buffer is still current. |
|
3416 |
(bury-buffer)))) |
|
3417 |
(helm-kill-async-processes) |
|
3418 |
;; Remove the temporary hooks added |
|
3419 |
;; by `with-helm-temp-hook' that |
|
3420 |
;; may not have been consumed. |
|
3421 |
(when helm--temp-hooks |
|
3422 |
(cl-loop for (fn . hook) in helm--temp-hooks |
|
3423 |
do (set hook (delete fn (symbol-value hook))))) |
|
3424 |
;; When running helm from a dedicated frame |
|
3425 |
;; with no minibuffer, helm will run in the main frame |
|
3426 |
;; which have a minibuffer, so be sure to disable |
|
3427 |
;; the `no-other-window' prop there. |
|
3428 |
(helm-prevent-switching-other-window :enabled nil) |
|
3429 |
(helm-log-run-hook 'helm-cleanup-hook) |
|
3430 |
(helm-frame-or-window-configuration 'restore) |
|
3431 |
;; [1] now bury-buffer from underlying windows otherwise, |
|
3432 |
;; if this window is killed the underlying buffer will |
|
3433 |
;; be a helm buffer. |
|
3434 |
(replace-buffer-in-windows helm-buffer) |
|
3435 |
(setq helm-alive-p nil) |
|
3436 |
;; Prevent error "No buffer named *helm*" triggered by |
|
3437 |
;; `helm-set-local-variable'. |
|
3438 |
(setq helm--force-updating-p nil) |
|
3439 |
(setq helm--buffer-in-new-frame-p nil) |
|
3440 |
;; This is needed in some cases where last input |
|
3441 |
;; is yielded infinitely in minibuffer after helm session. |
|
3442 |
(helm-clean-up-minibuffer)) |
|
3443 |
|
|
3444 |
(defun helm-clean-up-minibuffer () |
|
3445 |
"Remove contents of minibuffer." |
|
3446 |
(let ((miniwin (minibuffer-window))) |
|
3447 |
;; Clean only current minibuffer used by helm. |
|
3448 |
;; i.e The precedent one is active. |
|
3449 |
(unless (minibuffer-window-active-p miniwin) |
|
3450 |
(with-current-buffer (window-buffer miniwin) |
|
3451 |
(delete-minibuffer-contents))))) |
|
3452 |
|
|
3453 |
|
|
3454 |
;;; Input handling |
|
3455 |
;; |
|
3456 |
;; |
|
3457 |
(defun helm-check-minibuffer-input () |
|
3458 |
"Check minibuffer content." |
|
3459 |
(with-helm-quittable |
|
3460 |
(with-selected-window (or (active-minibuffer-window) |
|
3461 |
(minibuffer-window)) |
|
3462 |
(helm-check-new-input (minibuffer-contents))))) |
|
3463 |
|
|
3464 |
(defun helm-check-new-input (input) |
|
3465 |
"Check INPUT string and update the helm buffer if necessary." |
|
3466 |
(unless (equal input helm-pattern) |
|
3467 |
(setq helm-pattern input) |
|
3468 |
(unless (helm-action-window) |
|
3469 |
(setq helm-input helm-pattern)) |
|
3470 |
(helm-log "helm-pattern = %S" helm-pattern) |
|
3471 |
(helm-log "helm-input = %S" helm-input) |
|
3472 |
(setq helm--in-update t) |
|
3473 |
(helm-update))) |
|
3474 |
|
|
3475 |
(defun helm--reset-update-flag () |
|
3476 |
(run-with-idle-timer |
|
3477 |
helm-exit-idle-delay nil |
|
3478 |
(lambda () (setq helm--in-update nil)))) |
|
3479 |
|
|
3480 |
;; (add-hook 'helm-after-update-hook #'helm--reset-update-flag) |
|
3481 |
|
|
3482 |
|
|
3483 |
;; All candidates |
|
3484 |
|
|
3485 |
(defun helm-get-candidates (source) |
|
3486 |
"Retrieve and return the list of candidates from SOURCE." |
|
3487 |
(let* ((candidate-fn (assoc-default 'candidates source)) |
|
3488 |
(candidate-proc (assoc-default 'candidates-process source)) |
|
3489 |
(inhibit-quit candidate-proc) |
|
3490 |
cfn-error |
|
3491 |
(notify-error |
|
3492 |
(lambda (&optional e) |
|
3493 |
(error |
|
3494 |
"In `%s' source: `%s' %s %s" |
|
3495 |
(assoc-default 'name source) |
|
3496 |
(or candidate-fn candidate-proc) |
|
3497 |
(if e "\n" "must be a list, a symbol bound to a list, or a function returning a list") |
|
3498 |
(if e (prin1-to-string e) "")))) |
|
3499 |
(candidates (condition-case-unless-debug err |
|
3500 |
;; Process candidates-(process) function |
|
3501 |
;; It may return a process or a list of candidates. |
|
3502 |
(if candidate-proc |
|
3503 |
;; Calling `helm-interpret-value' with no |
|
3504 |
;; SOURCE arg force the use of `funcall' |
|
3505 |
;; and not `helm-apply-functions-from-source'. |
|
3506 |
(helm-interpret-value candidate-proc) |
|
3507 |
(helm-interpret-value candidate-fn source)) |
|
3508 |
(error (helm-log "Error: %S" (setq cfn-error err)) nil)))) |
|
3509 |
(cond ((and (processp candidates) (not candidate-proc)) |
|
3510 |
(warn "Candidates function `%s' should be called in a `candidates-process' attribute" |
|
3511 |
candidate-fn)) |
|
3512 |
((and candidate-proc (not (processp candidates))) |
|
3513 |
(error "Candidates function `%s' should run a process" candidate-proc))) |
|
3514 |
(cond ((processp candidates) |
|
3515 |
;; Candidates will be filtered later in process filter. |
|
3516 |
candidates) |
|
3517 |
;; An error occured in candidates function. |
|
3518 |
(cfn-error (unless helm--ignore-errors |
|
3519 |
(funcall notify-error cfn-error))) |
|
3520 |
;; Candidates function returns no candidates. |
|
3521 |
((or (null candidates) |
|
3522 |
;; Can happen when the output of a process |
|
3523 |
;; is empty, and the candidates function call |
|
3524 |
;; something like (split-string (buffer-string) "\n") |
|
3525 |
;; which result in a list of one empty string (Issue #938). |
|
3526 |
;; e.g (completing-read "test: " '("")) |
|
3527 |
(equal candidates '(""))) |
|
3528 |
nil) |
|
3529 |
((listp candidates) |
|
3530 |
;; Transform candidates with `candidate-transformer' functions or |
|
3531 |
;; `real-to-display' functions if those are found, |
|
3532 |
;; otherwise return candidates unmodified. |
|
3533 |
;; `filtered-candidate-transformer' is NOT called here. |
|
3534 |
(helm-transform-candidates candidates source)) |
|
3535 |
(t (funcall notify-error))))) |
|
3536 |
|
|
3537 |
(defmacro helm-while-no-input (&rest body) |
|
3538 |
"Same as `while-no-input' but without the `input-pending-p' test." |
|
3539 |
(declare (debug t) (indent 0)) |
|
3540 |
(let ((catch-sym (make-symbol "input"))) |
|
3541 |
`(with-local-quit |
|
3542 |
(catch ',catch-sym |
|
3543 |
(let ((throw-on-input ',catch-sym)) |
|
3544 |
,@body))))) |
|
3545 |
|
|
3546 |
(defun helm-get-cached-candidates (source) |
|
3547 |
"Return the cached value of candidates for SOURCE. |
|
3548 |
Cache the candidates if there is no cached value yet." |
|
3549 |
(let* ((name (assoc-default 'name source)) |
|
3550 |
(candidate-cache (gethash name helm-candidate-cache)) |
|
3551 |
(inhibit-quit (assoc-default 'candidates-process source))) |
|
3552 |
(helm-aif candidate-cache |
|
3553 |
(prog1 it (helm-log "Use cached candidates")) |
|
3554 |
(helm-log "No cached candidates, calculate candidates") |
|
3555 |
(let ((candidates (helm-get-candidates source))) |
|
3556 |
(cond ((processp candidates) |
|
3557 |
(push (cons candidates |
|
3558 |
(append source |
|
3559 |
(list (cons 'item-count 0) |
|
3560 |
(cons 'incomplete-line "")))) |
|
3561 |
helm-async-processes) |
|
3562 |
(set-process-filter candidates 'helm-output-filter) |
|
3563 |
(setq candidates nil)) |
|
3564 |
((not (assq 'volatile source)) |
|
3565 |
(puthash name candidates helm-candidate-cache))) |
|
3566 |
candidates)))) |
|
3567 |
|
|
3568 |
|
|
3569 |
;;; Candidate transformers |
|
3570 |
|
|
3571 |
(defun helm-process-candidate-transformer (candidates source) |
|
3572 |
"Execute `candidate-transformer' function(s) on CANDIDATES in SOURCE." |
|
3573 |
(helm-aif (assoc-default 'candidate-transformer source) |
|
3574 |
(helm-apply-functions-from-source source it candidates) |
|
3575 |
candidates)) |
|
3576 |
|
|
3577 |
(defun helm-process-filtered-candidate-transformer (candidates source) |
|
3578 |
"Execute `filtered-candidate-transformer' function(s) on CANDIDATES in SOURCE." |
|
3579 |
(helm-aif (assoc-default 'filtered-candidate-transformer source) |
|
3580 |
(helm-apply-functions-from-source source it candidates source) |
|
3581 |
candidates)) |
|
3582 |
|
|
3583 |
(defmacro helm--maybe-process-filter-one-by-one-candidate (candidate source) |
|
3584 |
"Execute `filter-one-by-one' function(s) on real value of CANDIDATE in SOURCE." |
|
3585 |
`(helm-aif (assoc-default 'filter-one-by-one ,source) |
|
3586 |
(let ((real (if (consp ,candidate) |
|
3587 |
(cdr ,candidate) |
|
3588 |
,candidate))) |
|
3589 |
(if (and (listp it) |
|
3590 |
(not (functionp it))) ;; Don't treat lambda's as list. |
|
3591 |
(cl-loop for f in it |
|
3592 |
do (setq ,candidate (funcall f real)) |
|
3593 |
finally return ,candidate) |
|
3594 |
(setq ,candidate (funcall it real)))) |
|
3595 |
,candidate)) |
|
3596 |
|
|
3597 |
(defun helm--initialize-one-by-one-candidates (candidates source) |
|
3598 |
"Process the CANDIDATES with the `filter-one-by-one' function in SOURCE. |
|
3599 |
Return CANDIDATES unchanged when pattern is not empty." |
|
3600 |
(helm-aif (and (string= helm-pattern "") |
|
3601 |
(assoc-default 'filter-one-by-one source)) |
|
3602 |
(cl-loop for cand in candidates collect |
|
3603 |
(helm--maybe-process-filter-one-by-one-candidate cand source)) |
|
3604 |
candidates)) |
|
3605 |
|
|
3606 |
(defun helm-process-filtered-candidate-transformer-maybe |
|
3607 |
(candidates source process-p) |
|
3608 |
"Execute `filtered-candidate-transformer' function(s) on CANDIDATES in SOURCE. |
|
3609 |
When PROCESS-P is non-`nil' execute `filtered-candidate-transformer' |
|
3610 |
functions if some, otherwise return CANDIDATES." |
|
3611 |
(if process-p |
|
3612 |
;; When no filter return CANDIDATES unmodified. |
|
3613 |
(helm-process-filtered-candidate-transformer candidates source) |
|
3614 |
candidates)) |
|
3615 |
|
|
3616 |
(defun helm-process-real-to-display (candidates source) |
|
3617 |
"Execute real-to-display function on all CANDIDATES of SOURCE." |
|
3618 |
(helm-aif (assoc-default 'real-to-display source) |
|
3619 |
(setq candidates (helm-apply-functions-from-source |
|
3620 |
source 'mapcar |
|
3621 |
(lambda (cand) |
|
3622 |
(if (consp cand) |
|
3623 |
;; override DISPLAY from candidate-transformer |
|
3624 |
(cons (funcall it (cdr cand)) (cdr cand)) |
|
3625 |
(cons (funcall it cand) cand))) |
|
3626 |
candidates)) |
|
3627 |
candidates)) |
|
3628 |
|
|
3629 |
(defun helm-transform-candidates (candidates source &optional process-p) |
|
3630 |
"Transform CANDIDATES from SOURCE according to candidate transformers. |
|
3631 |
|
|
3632 |
When PROCESS-P is non-`nil' executes the |
|
3633 |
`filtered-candidate-transformer' functions, otherwise processes |
|
3634 |
`candidate-transformer' functions only, |
|
3635 |
`filtered-candidate-transformer' functions being processed later, |
|
3636 |
after the candidates have been narrowed by |
|
3637 |
`helm-candidate-number-limit', see `helm-compute-matches'. When |
|
3638 |
`real-to-display' attribute is present, execute its functions on all |
|
3639 |
maybe filtered CANDIDATES." |
|
3640 |
(helm-process-real-to-display |
|
3641 |
(helm-process-filtered-candidate-transformer-maybe |
|
3642 |
(helm-process-candidate-transformer |
|
3643 |
candidates source) |
|
3644 |
source process-p) |
|
3645 |
source)) |
|
3646 |
|
|
3647 |
|
|
3648 |
;; Narrowing candidates |
|
3649 |
(defun helm-candidate-number-limit (source) |
|
3650 |
"Apply candidate-number-limit attribute value. |
|
3651 |
This overrides `helm-candidate-number-limit' variable. |
|
3652 |
|
|
3653 |
e.g: |
|
3654 |
If \(candidate-number-limit\) is in SOURCE, show all candidates in SOURCE. |
|
3655 |
If \(candidate-number-limit . 123\) is in SOURCE limit candidate to 123." |
|
3656 |
(helm-aif (assq 'candidate-number-limit source) |
|
3657 |
;; When assoc value is nil use by default 99999999 otherwise use |
|
3658 |
;; the assoc value, when it is a symbol interpret its value (#1831). |
|
3659 |
(or (helm-aand (cdr it) (helm-interpret-value it)) 99999999) |
|
3660 |
(or helm-candidate-number-limit 99999999))) |
|
3661 |
|
|
3662 |
(defun helm-candidate-get-display (candidate) |
|
3663 |
"Get searched display part from CANDIDATE. |
|
3664 |
CANDIDATE is either a string, a symbol, or a \(DISPLAY . REAL\) |
|
3665 |
cons cell." |
|
3666 |
(cond ((car-safe candidate)) |
|
3667 |
((symbolp candidate) |
|
3668 |
(symbol-name candidate)) |
|
3669 |
((numberp candidate) |
|
3670 |
(number-to-string candidate)) |
|
3671 |
(t candidate))) |
|
3672 |
|
|
3673 |
(defun helm-process-pattern-transformer (pattern source) |
|
3674 |
"Execute pattern-transformer attribute function(s) on PATTERN in SOURCE." |
|
3675 |
(helm-aif (assoc-default 'pattern-transformer source) |
|
3676 |
(helm-apply-functions-from-source source it pattern) |
|
3677 |
pattern)) |
|
3678 |
|
|
3679 |
(defun helm-default-match-function (candidate) |
|
3680 |
"Check if `helm-pattern' match CANDIDATE. |
|
3681 |
Default function to match candidates according to `helm-pattern'." |
|
3682 |
(string-match helm-pattern candidate)) |
|
3683 |
|
|
3684 |
|
|
3685 |
;;; Fuzzy matching |
|
3686 |
;; |
|
3687 |
;; |
|
3688 |
(defvar helm--fuzzy-regexp-cache (make-hash-table :test 'eq)) |
|
3689 |
(defun helm--fuzzy-match-maybe-set-pattern () |
|
3690 |
;; Computing helm-pattern with helm--mapconcat-pattern |
|
3691 |
;; is costly, so cache it once time for all and reuse it |
|
3692 |
;; until pattern change. |
|
3693 |
(when helm--in-fuzzy |
|
3694 |
(let ((fun (if (string-match "\\`\\^" helm-pattern) |
|
3695 |
#'identity |
|
3696 |
#'helm--mapconcat-pattern))) |
|
3697 |
(clrhash helm--fuzzy-regexp-cache) |
|
3698 |
;; FIXME: Splitted part are not handled here, |
|
3699 |
;; I must compute them in `helm-search-match-part' |
|
3700 |
;; when negation and in-buffer are used. |
|
3701 |
(if (string-match "\\`!" helm-pattern) |
|
3702 |
(puthash 'helm-pattern |
|
3703 |
(if (> (length helm-pattern) 1) |
|
3704 |
(list (funcall fun (substring helm-pattern 1 2)) |
|
3705 |
(funcall fun (substring helm-pattern 1))) |
|
3706 |
'("" "")) |
|
3707 |
helm--fuzzy-regexp-cache) |
|
3708 |
(puthash 'helm-pattern |
|
3709 |
(if (> (length helm-pattern) 0) |
|
3710 |
(list (funcall fun (substring helm-pattern 0 1)) |
|
3711 |
(funcall fun helm-pattern)) |
|
3712 |
'("" "")) |
|
3713 |
helm--fuzzy-regexp-cache))))) |
|
3714 |
|
|
3715 |
(defun helm-fuzzy-match (candidate) |
|
3716 |
"Check if `helm-pattern' fuzzy matches CANDIDATE. |
|
3717 |
This function is used with sources built with `helm-source-sync'." |
|
3718 |
(unless (string-match " " helm-pattern) |
|
3719 |
;; When pattern have one or more spaces, let |
|
3720 |
;; multi-match doing the job with no fuzzy matching.[1] |
|
3721 |
(let ((regexp (cadr (gethash 'helm-pattern helm--fuzzy-regexp-cache)))) |
|
3722 |
(if (string-match "\\`!" helm-pattern) |
|
3723 |
(not (string-match regexp candidate)) |
|
3724 |
(string-match regexp candidate))))) |
|
3725 |
|
|
3726 |
(defun helm-fuzzy-search (pattern) |
|
3727 |
"Same as `helm-fuzzy-match' but for sources built with |
|
3728 |
`helm-source-in-buffer'." |
|
3729 |
(unless (string-match " " helm-pattern) |
|
3730 |
;; Same as in `helm-fuzzy-match' ref[1]. |
|
3731 |
(let* ((regexps (gethash 'helm-pattern helm--fuzzy-regexp-cache)) |
|
3732 |
(partial-regexp (car regexps)) |
|
3733 |
(regexp (cadr regexps))) |
|
3734 |
(if (string-match "\\`!" pattern) |
|
3735 |
;; Don't try to search here, just return |
|
3736 |
;; the position of line and go ahead, |
|
3737 |
;; letting `helm-search-match-part' checking if |
|
3738 |
;; pattern match against this line. |
|
3739 |
(prog1 (list (point-at-bol) (point-at-eol)) |
|
3740 |
(forward-line 1)) |
|
3741 |
;; We could use here directly `re-search-forward' |
|
3742 |
;; on the regexp produced by `helm--mapconcat-pattern', |
|
3743 |
;; but it is very slow because emacs have to do an incredible |
|
3744 |
;; amount of loops to match e.g "[^f]*f[^o]*o..." in the whole buffer, |
|
3745 |
;; more the regexp is long more the amount of loops grow. |
|
3746 |
;; (Probably leading to a max-lisp-eval-depth error if both |
|
3747 |
;; regexp and buffer are too big) |
|
3748 |
;; So just search the first bit of pattern e.g "[^f]*f", and |
|
3749 |
;; then search the corresponding line with the whole regexp, |
|
3750 |
;; which increase dramatically the speed of the search. |
|
3751 |
(cl-loop while (re-search-forward partial-regexp nil t) |
|
3752 |
for bol = (point-at-bol) |
|
3753 |
for eol = (point-at-eol) |
|
3754 |
if (progn (goto-char bol) |
|
3755 |
(re-search-forward regexp eol t)) |
|
3756 |
do (goto-char eol) and return t |
|
3757 |
else do (goto-char eol) |
|
3758 |
finally return nil))))) |
|
3759 |
|
|
3760 |
(defun helm-score-candidate-for-pattern (candidate pattern) |
|
3761 |
"Assign score to CANDIDATE according to PATTERN. |
|
3762 |
Score is calculated for contiguous matches found with PATTERN. |
|
3763 |
Score is 100 (maximum) if PATTERN is fully matched in CANDIDATE. |
|
3764 |
One point bonus is added to score when PATTERN prefix matches |
|
3765 |
CANDIDATE. Contiguous matches get a coefficient of 2." |
|
3766 |
(let* ((cand (if (stringp candidate) |
|
3767 |
candidate (helm-stringify candidate))) |
|
3768 |
(pat-lookup (helm--collect-pairs-in-string pattern)) |
|
3769 |
(str-lookup (helm--collect-pairs-in-string cand)) |
|
3770 |
(bonus (cond ((equal (car pat-lookup) (car str-lookup)) |
|
3771 |
1) |
|
3772 |
((and (null pat-lookup) ; length = 1 |
|
3773 |
(string= pattern (substring cand 0 1))) |
|
3774 |
150) |
|
3775 |
(t 0))) |
|
3776 |
(bonus1 (and (string-match (concat "\\<" (regexp-quote pattern) "\\>") |
|
3777 |
cand) |
|
3778 |
100))) |
|
3779 |
(+ bonus (or bonus1 |
|
3780 |
;; Give a coefficient of 2 for contiguous matches. |
|
3781 |
;; That's mean that "wiaaaki" will not take precedence |
|
3782 |
;; on "aaawiki" when matching on "wiki" even if "wiaaaki" |
|
3783 |
;; starts by "wi". |
|
3784 |
(* (length (cl-nintersection |
|
3785 |
pat-lookup str-lookup :test 'equal)) |
|
3786 |
2))))) |
|
3787 |
|
|
3788 |
(defun helm-fuzzy-matching-default-sort-fn-1 (candidates &optional use-real basename preserve-tie-order) |
|
3789 |
"The transformer for sorting candidates in fuzzy matching. |
|
3790 |
It sorts on the display part by default. |
|
3791 |
|
|
3792 |
Sorts CANDIDATES by their scores as calculated by |
|
3793 |
`helm-score-candidate-for-pattern'. Set USE-REAL to non-`nil' to |
|
3794 |
sort on the real part. If BASENAME is non-nil assume we are |
|
3795 |
completing filenames and sort on basename of candidates. If |
|
3796 |
PRESERVE-TIE-ORDER is nil, ties in scores are sorted by length of |
|
3797 |
the candidates." |
|
3798 |
(if (string= helm-pattern "") |
|
3799 |
candidates |
|
3800 |
(let ((table-scr (make-hash-table :test 'equal))) |
|
3801 |
(sort candidates |
|
3802 |
(lambda (s1 s2) |
|
3803 |
;; Score and measure the length on real or display part of candidate |
|
3804 |
;; according to `use-real'. |
|
3805 |
(let* ((real-or-disp-fn (if use-real #'cdr #'car)) |
|
3806 |
(cand1 (cond ((and basename (consp s1)) |
|
3807 |
(helm-basename (funcall real-or-disp-fn s1))) |
|
3808 |
((consp s1) (funcall real-or-disp-fn s1)) |
|
3809 |
(basename (helm-basename s1)) |
|
3810 |
(t s1))) |
|
3811 |
(cand2 (cond ((and basename (consp s2)) |
|
3812 |
(helm-basename (funcall real-or-disp-fn s2))) |
|
3813 |
((consp s2) (funcall real-or-disp-fn s2)) |
|
3814 |
(basename (helm-basename s2)) |
|
3815 |
(t s2))) |
|
3816 |
(data1 (or (gethash cand1 table-scr) |
|
3817 |
(puthash cand1 |
|
3818 |
(list (helm-score-candidate-for-pattern |
|
3819 |
cand1 helm-pattern) |
|
3820 |
(length (helm-stringify cand1))) |
|
3821 |
table-scr))) |
|
3822 |
(data2 (or (gethash cand2 table-scr) |
|
3823 |
(puthash cand2 |
|
3824 |
(list (helm-score-candidate-for-pattern |
|
3825 |
cand2 helm-pattern) |
|
3826 |
(length (helm-stringify cand2))) |
|
3827 |
table-scr))) |
|
3828 |
(len1 (cadr data1)) |
|
3829 |
(len2 (cadr data2)) |
|
3830 |
(scr1 (car data1)) |
|
3831 |
(scr2 (car data2))) |
|
3832 |
(cond ((= scr1 scr2) |
|
3833 |
(unless preserve-tie-order |
|
3834 |
(< len1 len2))) |
|
3835 |
((> scr1 scr2))))))))) |
|
3836 |
|
|
3837 |
(defun helm-fuzzy-matching-default-sort-fn (candidates _source) |
|
3838 |
"Default `filtered-candidate-transformer' to sort candidates in fuzzy matching." |
|
3839 |
(helm-fuzzy-matching-default-sort-fn-1 candidates)) |
|
3840 |
|
|
3841 |
(defun helm-fuzzy-matching-sort-fn-preserve-ties-order (candidates _source) |
|
3842 |
"`filtered-candidate-transformer' to sort candidates in fuzzy matching, preserving order of ties. |
|
3843 |
The default function, `helm-fuzzy-matching-default-sort-fn', |
|
3844 |
sorts ties by length, shortest first. This function may be more |
|
3845 |
useful when the order of the candidates is meaningful, e.g. with |
|
3846 |
`recentf-list'." |
|
3847 |
(helm-fuzzy-matching-default-sort-fn-1 candidates nil t)) |
|
3848 |
|
|
3849 |
(defun helm--maybe-get-migemo-pattern (pattern) |
|
3850 |
(or (and helm-migemo-mode |
|
3851 |
(assoc-default pattern helm-mm--previous-migemo-info)) |
|
3852 |
pattern)) |
|
3853 |
|
|
3854 |
(defun helm-fuzzy-default-highlight-match (candidate) |
|
3855 |
"The default function to highlight matches in fuzzy matching. |
|
3856 |
Highlight elements in CANDIDATE matching `helm-pattern' according |
|
3857 |
to the matching method in use." |
|
3858 |
(if (string= helm-pattern "") |
|
3859 |
;; Empty pattern, do nothing. |
|
3860 |
candidate |
|
3861 |
;; Else start highlighting. |
|
3862 |
(let* ((pair (and (consp candidate) candidate)) |
|
3863 |
(display (helm-stringify (if pair (car pair) candidate))) |
|
3864 |
(real (cdr pair)) |
|
3865 |
(regex (helm--maybe-get-migemo-pattern helm-pattern)) |
|
3866 |
(mp (pcase (get-text-property 0 'match-part display) |
|
3867 |
((pred (string= display)) nil) |
|
3868 |
(str str))) |
|
3869 |
(count 0) |
|
3870 |
beg-str end-str) |
|
3871 |
;; Extract all parts of display keeping original properties. |
|
3872 |
(when (and mp (string-match (regexp-quote mp) display)) |
|
3873 |
(setq beg-str (substring display 0 (match-beginning 0)) |
|
3874 |
end-str (substring display (match-end 0) (length display)) |
|
3875 |
mp (substring display (match-beginning 0) (match-end 0)))) |
|
3876 |
(with-temp-buffer |
|
3877 |
;; Insert the whole display part and remove non--match-part |
|
3878 |
;; to keep their original face properties. |
|
3879 |
(insert (propertize (or mp display) 'read-only nil)) ; Fix (#1176) |
|
3880 |
(goto-char (point-min)) |
|
3881 |
(condition-case nil |
|
3882 |
(progn |
|
3883 |
;; Try first matching against whole pattern. |
|
3884 |
(while (re-search-forward regex nil t) |
|
3885 |
(cl-incf count) |
|
3886 |
(helm-add-face-text-properties |
|
3887 |
(match-beginning 0) (match-end 0) 'helm-match)) |
|
3888 |
;; If no matches start matching against multiples or fuzzy matches. |
|
3889 |
(when (zerop count) |
|
3890 |
(cl-loop with multi-match = (string-match-p " " helm-pattern) |
|
3891 |
with patterns = (if multi-match |
|
3892 |
(mapcar #'helm--maybe-get-migemo-pattern |
|
3893 |
(helm-mm-split-pattern helm-pattern)) |
|
3894 |
(split-string helm-pattern "" t)) |
|
3895 |
for p in patterns |
|
3896 |
;; Multi matches (regexps patterns). |
|
3897 |
if multi-match do |
|
3898 |
(progn |
|
3899 |
(while (re-search-forward p nil t) |
|
3900 |
(helm-add-face-text-properties |
|
3901 |
(match-beginning 0) (match-end 0) |
|
3902 |
'helm-match)) |
|
3903 |
(goto-char (point-min))) |
|
3904 |
;; Fuzzy matches (literal patterns). |
|
3905 |
else do |
|
3906 |
(when (search-forward p nil t) |
|
3907 |
(helm-add-face-text-properties |
|
3908 |
(match-beginning 0) (match-end 0) |
|
3909 |
'helm-match))))) |
|
3910 |
(invalid-regexp nil)) |
|
3911 |
;; Now replace the original match-part with the part |
|
3912 |
;; with face properties added. |
|
3913 |
(setq display (if mp (concat beg-str (buffer-string) end-str) (buffer-string)))) |
|
3914 |
(if real (cons display real) display)))) |
|
3915 |
|
|
3916 |
(defun helm-fuzzy-highlight-matches (candidates _source) |
|
3917 |
"The filtered-candidate-transformer function to highlight fuzzy matches. |
|
3918 |
See `helm-fuzzy-default-highlight-match'." |
|
3919 |
(cl-loop for c in candidates |
|
3920 |
collect (funcall helm-fuzzy-matching-highlight-fn c))) |
|
3921 |
|
|
3922 |
|
|
3923 |
;;; Matching candidates |
|
3924 |
;; |
|
3925 |
;; |
|
3926 |
(defun helm-match-functions (source) |
|
3927 |
(let ((matchfns (or (assoc-default 'match source) |
|
3928 |
(assoc-default 'match-strict source) |
|
3929 |
#'helm-default-match-function))) |
|
3930 |
(if (and (listp matchfns) (not (functionp matchfns))) |
|
3931 |
matchfns (list matchfns)))) |
|
3932 |
|
|
3933 |
(defun helm-search-functions (source) |
|
3934 |
(let ((searchfns (assoc-default 'search source))) |
|
3935 |
(if (and (listp searchfns) (not (functionp searchfns))) |
|
3936 |
searchfns (list searchfns)))) |
|
3937 |
|
|
3938 |
(defun helm-take-first-elements (seq n) |
|
3939 |
"Return the first N elements of SEQ if SEQ is longer than N. |
|
3940 |
It is used for narrowing list of candidates to the |
|
3941 |
`helm-candidate-number-limit'." |
|
3942 |
(if (> (length seq) n) (cl-subseq seq 0 n) seq)) |
|
3943 |
|
|
3944 |
(defun helm-match-from-candidates (cands matchfns match-part-fn limit source) |
|
3945 |
(condition-case-unless-debug err |
|
3946 |
(cl-loop with hash = (make-hash-table :test 'equal) |
|
3947 |
with allow-dups = (assq 'allow-dups source) |
|
3948 |
with case-fold-search = (helm-set-case-fold-search) |
|
3949 |
with count = 0 |
|
3950 |
for iter from 1 |
|
3951 |
for fn in matchfns |
|
3952 |
when (< count limit) nconc |
|
3953 |
(cl-loop for c in cands |
|
3954 |
for dup = (gethash c hash) |
|
3955 |
while (< count limit) |
|
3956 |
for target = (helm-candidate-get-display c) |
|
3957 |
for prop-part = (get-text-property 0 'match-part target) |
|
3958 |
for part = (and match-part-fn |
|
3959 |
(or prop-part |
|
3960 |
(funcall match-part-fn target))) |
|
3961 |
;; When allowing dups check if DUP |
|
3962 |
;; have been already found in previous loop |
|
3963 |
;; by comparing its value with ITER. |
|
3964 |
when (and (or (and allow-dups dup (= dup iter)) |
|
3965 |
(null dup)) |
|
3966 |
(condition-case nil |
|
3967 |
(funcall fn (or part target)) |
|
3968 |
(invalid-regexp nil))) |
|
3969 |
do |
|
3970 |
(progn |
|
3971 |
;; Give as value the iteration number of |
|
3972 |
;; inner loop to be able to check if |
|
3973 |
;; the duplicate have not been found in previous loop. |
|
3974 |
(puthash c iter hash) |
|
3975 |
(helm--maybe-process-filter-one-by-one-candidate c source) |
|
3976 |
(cl-incf count)) |
|
3977 |
;; Filter out nil candidates maybe returned by |
|
3978 |
;; `helm--maybe-process-filter-one-by-one-candidate'. |
|
3979 |
and when c collect |
|
3980 |
(if (and part (not prop-part)) |
|
3981 |
(if (consp c) |
|
3982 |
(cons (propertize target 'match-part part) (cdr c)) |
|
3983 |
(propertize c 'match-part part)) |
|
3984 |
c))) |
|
3985 |
(error (unless (eq (car err) 'invalid-regexp) ; Always ignore regexps errors. |
|
3986 |
(helm-log-error "helm-match-from-candidates in source `%s': %s %s" |
|
3987 |
(assoc-default 'name source) (car err) (cdr err))) |
|
3988 |
nil))) |
|
3989 |
|
|
3990 |
(defun helm-compute-matches (source) |
|
3991 |
"Start computing candidates in SOURCE." |
|
3992 |
(save-current-buffer |
|
3993 |
(let ((matchfns (helm-match-functions source)) |
|
3994 |
(matchpartfn (assoc-default 'match-part source)) |
|
3995 |
(helm--source-name (assoc-default 'name source)) |
|
3996 |
(helm-current-source source) |
|
3997 |
(limit (helm-candidate-number-limit source)) |
|
3998 |
(helm-pattern (helm-process-pattern-transformer |
|
3999 |
helm-pattern source))) |
|
4000 |
(helm--fuzzy-match-maybe-set-pattern) |
|
4001 |
;; If source have a `filtered-candidate-transformer' attr |
|
4002 |
;; Filter candidates with this func, otherwise just compute |
|
4003 |
;; candidates. |
|
4004 |
;; NOTE that this next block of code is returning nil on async sources, |
|
4005 |
;; the candidates being processed directly in `helm-output-filter' |
|
4006 |
;; process-filter. |
|
4007 |
(helm-process-filtered-candidate-transformer |
|
4008 |
;; Using in-buffer method or helm-pattern is empty |
|
4009 |
;; in this case compute all candidates. |
|
4010 |
(if (or (equal helm-pattern "") |
|
4011 |
(helm--candidates-in-buffer-p matchfns)) |
|
4012 |
;; Compute all candidates up to LIMIT. |
|
4013 |
;; one-by-one are computed here only for sources that |
|
4014 |
;; display a list of candidates even with an empty |
|
4015 |
;; pattern. |
|
4016 |
(helm--initialize-one-by-one-candidates |
|
4017 |
(helm-take-first-elements |
|
4018 |
(helm-get-cached-candidates source) limit) |
|
4019 |
source) |
|
4020 |
;; Compute candidates according to pattern with their match |
|
4021 |
;; fns. |
|
4022 |
;; one-by-one filtered candidates are computed during the |
|
4023 |
;; execution of next loop in `helm-match-from-candidates'. |
|
4024 |
(helm-match-from-candidates |
|
4025 |
(helm-get-cached-candidates source) matchfns matchpartfn limit source)) |
|
4026 |
source)))) |
|
4027 |
|
|
4028 |
(defun helm--candidates-in-buffer-p (matchfns) |
|
4029 |
(equal matchfns '(identity))) |
|
4030 |
|
|
4031 |
(defun helm-render-source (source matches) |
|
4032 |
"Display MATCHES from SOURCE according to its settings." |
|
4033 |
(helm-log "Source name = %S" (assoc-default 'name source)) |
|
4034 |
(when matches |
|
4035 |
(helm-insert-header-from-source source) |
|
4036 |
(cl-loop with separate = nil |
|
4037 |
with start = (point) |
|
4038 |
with singleline = (null (assq 'multiline source)) |
|
4039 |
for m in matches |
|
4040 |
for count from 1 |
|
4041 |
if singleline |
|
4042 |
do (helm-insert-match m 'insert count source) |
|
4043 |
else |
|
4044 |
do (progn |
|
4045 |
(if separate |
|
4046 |
(helm-insert-candidate-separator) |
|
4047 |
(setq separate t)) |
|
4048 |
(helm-insert-match m 'insert count source)) |
|
4049 |
finally (and (null singleline) |
|
4050 |
(put-text-property start (point) |
|
4051 |
'helm-multiline t))))) |
|
4052 |
|
|
4053 |
(defmacro helm--maybe-use-while-no-input (&rest body) |
|
4054 |
"Wrap BODY in `helm-while-no-input' unless initializing a remote connection." |
|
4055 |
`(progn |
|
4056 |
(if (and (file-remote-p helm-pattern) |
|
4057 |
(not (file-remote-p helm-pattern nil t))) |
|
4058 |
;; Tramp will ask for passwd, don't use `helm-while-no-input'. |
|
4059 |
,@body |
|
4060 |
(helm-log "Using here `helm-while-no-input'") |
|
4061 |
(helm-while-no-input ,@body)))) |
|
4062 |
|
|
4063 |
(defun helm--collect-matches (src-list) |
|
4064 |
"Returns a list of matches for each source in SRC-LIST. |
|
4065 |
|
|
4066 |
The resulting value is a list of lists, e.g ((a b c) (c d) (e f)) or |
|
4067 |
\(nil nil nil) for three sources when no matches found, however this |
|
4068 |
function can be interrupted by new input and in this case returns a |
|
4069 |
plain `nil' i.e not (nil), in this case `helm-update' is not rendering |
|
4070 |
the source, keeping previous candidates in display." |
|
4071 |
(let ((matches (helm--maybe-use-while-no-input |
|
4072 |
(cl-loop for src in src-list |
|
4073 |
collect (helm-compute-matches src))))) |
|
4074 |
(unless (eq matches t) matches))) |
|
4075 |
|
|
4076 |
|
|
4077 |
;;; Case fold search |
|
4078 |
;; |
|
4079 |
;; |
|
4080 |
(cl-defun helm-set-case-fold-search (&optional (pattern helm-pattern)) |
|
4081 |
"Used to set the value of `case-fold-search' in helm. |
|
4082 |
Return t or nil depending on the value of `helm-case-fold-search' |
|
4083 |
and `helm-pattern'." |
|
4084 |
(let ((helm-case-fold-search |
|
4085 |
(helm-aif (assq 'case-fold-search (helm-get-current-source)) |
|
4086 |
(cdr it) |
|
4087 |
helm-case-fold-search)) |
|
4088 |
;; Only parse basename for filenames |
|
4089 |
;; to avoid setting case sensitivity |
|
4090 |
;; when expanded directories contains upcase |
|
4091 |
;; characters. |
|
4092 |
(bn-or-pattern (if (string-match "[~/]*" pattern) |
|
4093 |
(helm-basename pattern) |
|
4094 |
pattern))) |
|
4095 |
(helm-set-case-fold-search-1 bn-or-pattern))) |
|
4096 |
|
|
4097 |
(defun helm-set-case-fold-search-1 (pattern) |
|
4098 |
(cl-case helm-case-fold-search |
|
4099 |
(smart (let ((case-fold-search nil)) |
|
4100 |
(if (string-match "[[:upper:]]" pattern) nil t))) |
|
4101 |
(t helm-case-fold-search))) |
|
4102 |
|
|
4103 |
|
|
4104 |
;;; Helm update |
|
4105 |
;; |
|
4106 |
(defun helm-update (&optional preselect source candidates) |
|
4107 |
"Update candidates list in `helm-buffer' based on `helm-pattern'. |
|
4108 |
Argument PRESELECT is a string or regexp used to move selection |
|
4109 |
to a particular place after finishing update. |
|
4110 |
When SOURCE is provided update mode-line for this source, otherwise |
|
4111 |
the current source will be used. |
|
4112 |
Argument CANDIDATES when provided is used to redisplay these candidates |
|
4113 |
without recomputing them, it should be a list of lists." |
|
4114 |
(helm-log "Start updating") |
|
4115 |
(helm-kill-async-processes) |
|
4116 |
;; When persistent action have been called |
|
4117 |
;; we have two windows even with `helm-full-frame'. |
|
4118 |
;; So go back to one window when updating if `helm-full-frame' |
|
4119 |
;; is non-`nil'. |
|
4120 |
(when (with-helm-buffer |
|
4121 |
(and helm-onewindow-p |
|
4122 |
;; We are not displaying helm-buffer in a frame and |
|
4123 |
;; helm-window is already displayed. |
|
4124 |
(not helm--buffer-in-new-frame-p) |
|
4125 |
(helm-window) |
|
4126 |
(not (helm-action-window)))) |
|
4127 |
(with-helm-window (delete-other-windows))) |
|
4128 |
(with-current-buffer (helm-buffer-get) |
|
4129 |
(set (make-local-variable 'helm-input-local) helm-pattern) |
|
4130 |
(unwind-protect |
|
4131 |
(let (sources matches) |
|
4132 |
;; Collect sources ready to be updated. |
|
4133 |
(setq sources |
|
4134 |
(cl-loop for src in helm-sources |
|
4135 |
when (helm-update-source-p src) |
|
4136 |
collect src)) |
|
4137 |
;; When no sources to update erase buffer |
|
4138 |
;; to avoid duplication of header and candidates |
|
4139 |
;; when next chunk of update will arrive, |
|
4140 |
;; otherwise the buffer is erased AFTER [1] the results |
|
4141 |
;; are computed. |
|
4142 |
(unless sources (erase-buffer)) |
|
4143 |
;; Compute matches without rendering the sources. |
|
4144 |
;; This prevent the helm-buffer flickering when constantly |
|
4145 |
;; updating. |
|
4146 |
(helm-log "Matches: %S" |
|
4147 |
(setq matches (or candidates (helm--collect-matches sources)))) |
|
4148 |
;; If computing matches finished and is not interrupted |
|
4149 |
;; erase the helm-buffer and render results (Fix #1157). |
|
4150 |
(when matches ;; nil only when interrupted by helm-while-no-input. |
|
4151 |
(erase-buffer) ; [1] |
|
4152 |
(cl-loop for src in sources |
|
4153 |
for mtc in matches |
|
4154 |
do (helm-render-source src mtc)) |
|
4155 |
;; Move to first line only when there is matches |
|
4156 |
;; to avoid cursor moving upside down (issue #1703). |
|
4157 |
(helm--update-move-first-line) |
|
4158 |
(helm--reset-update-flag))) |
|
4159 |
;; When there is only one async source, update mode-line and run |
|
4160 |
;; `helm-after-update-hook' in `helm-output-filter--post-process', |
|
4161 |
;; when there is more than one source, update mode-line and run |
|
4162 |
;; `helm-after-update-hook' now even if an async source is |
|
4163 |
;; present and running in BG. |
|
4164 |
(let ((src (or source (helm-get-current-source)))) |
|
4165 |
(unless (assq 'candidates-process src) |
|
4166 |
(and src (helm-display-mode-line src 'force)) |
|
4167 |
(helm-log-run-hook 'helm-after-update-hook))) |
|
4168 |
(when preselect |
|
4169 |
(helm-log "Update preselect candidate %s" preselect) |
|
4170 |
(if (helm-window) |
|
4171 |
(with-helm-window (helm-preselect preselect source)) |
|
4172 |
(helm-preselect preselect source))) |
|
4173 |
(setq helm--force-updating-p nil)) |
|
4174 |
(helm-log "end update"))) |
|
4175 |
|
|
4176 |
(defun helm-update-source-p (source) |
|
4177 |
"Whether SOURCE needs updating or not." |
|
4178 |
(let ((len (string-width |
|
4179 |
(if (assq 'multimatch source) |
|
4180 |
;; Don't count spaces entered when using |
|
4181 |
;; multi-match. |
|
4182 |
(replace-regexp-in-string " " "" helm-pattern) |
|
4183 |
helm-pattern)))) |
|
4184 |
(and (or (not helm-source-filter) |
|
4185 |
(member (assoc-default 'name source) helm-source-filter)) |
|
4186 |
(>= len |
|
4187 |
(helm-aif (assq 'requires-pattern source) (or (cdr it) 1) 0)) |
|
4188 |
;; Entering repeatedly these strings (*, ?) takes 100% CPU |
|
4189 |
;; and hang emacs on MacOs preventing deleting backward those |
|
4190 |
;; characters (issue #1802). |
|
4191 |
(not (string-match-p "\\`[*]+\\'" helm-pattern)) |
|
4192 |
;; These incomplete regexps hang helm forever |
|
4193 |
;; so defer update. Maybe replace spaces quoted when using |
|
4194 |
;; multi-match. |
|
4195 |
(not (member (replace-regexp-in-string "\\s\\ " " " helm-pattern) |
|
4196 |
helm-update-blacklist-regexps))))) |
|
4197 |
|
|
4198 |
(defun helm--update-move-first-line () |
|
4199 |
"Goto first line of `helm-buffer'." |
|
4200 |
(goto-char (point-min)) |
|
4201 |
(if (helm-window) |
|
4202 |
(helm-move-selection-common :where 'line |
|
4203 |
:direction 'next |
|
4204 |
:follow t) |
|
4205 |
(forward-line 1) |
|
4206 |
(helm-mark-current-line) |
|
4207 |
(helm-follow-execute-persistent-action-maybe))) |
|
4208 |
|
|
4209 |
(cl-defun helm-force-update (&optional preselect (recenter t)) |
|
4210 |
"Force recalculation and update of candidates. |
|
4211 |
|
|
4212 |
Unlike `helm-update', this function re-evaluates `init' and |
|
4213 |
`update' attributes when present; also `helm-candidate-cache' is |
|
4214 |
not reinitialized, meaning candidates are not recomputed unless |
|
4215 |
pattern has changed. |
|
4216 |
|
|
4217 |
Selection is preserved to current candidate if it still exists after |
|
4218 |
update or moved to PRESELECT, if specified. |
|
4219 |
The helm-window is recentered at the end when RECENTER is `t' |
|
4220 |
which is the default, RECENTER can be also a number in this case it is |
|
4221 |
passed as argument to `recenter'." |
|
4222 |
(with-helm-buffer |
|
4223 |
(let* ((source (helm-get-current-source)) |
|
4224 |
(selection (helm-aif (helm-get-selection nil t source) |
|
4225 |
(regexp-quote it)))) |
|
4226 |
(setq helm--force-updating-p t) |
|
4227 |
(mapc 'helm-force-update--reinit helm-sources) |
|
4228 |
(helm-update (or preselect selection) source) |
|
4229 |
(when (and (helm-window) recenter) |
|
4230 |
(with-helm-window |
|
4231 |
(recenter (and (numberp recenter) recenter))))))) |
|
4232 |
|
|
4233 |
(defun helm-refresh () |
|
4234 |
"Force recalculation and update of candidates." |
|
4235 |
(interactive) |
|
4236 |
(with-helm-alive-p |
|
4237 |
(helm-force-update))) |
|
4238 |
(put 'helm-refresh 'helm-only t) |
|
4239 |
|
|
4240 |
(defun helm-force-update--reinit (source) |
|
4241 |
"Reinit SOURCE by calling its update and init functions." |
|
4242 |
;; When using a specific buffer as cache, don't kill it. |
|
4243 |
(helm-aif (and (null (bufferp (assoc-default |
|
4244 |
(helm-attr 'name source) |
|
4245 |
helm--candidate-buffer-alist))) |
|
4246 |
(helm-apply-functions-from-source |
|
4247 |
source 'helm-candidate-buffer)) |
|
4248 |
(kill-buffer it)) |
|
4249 |
(cl-dolist (attr '(update init)) |
|
4250 |
(helm-aif (assoc-default attr source) |
|
4251 |
(helm-apply-functions-from-source source it))) |
|
4252 |
(helm-remove-candidate-cache source)) |
|
4253 |
|
|
4254 |
(defun helm-redisplay-buffer () |
|
4255 |
"Redisplay candidates in `helm-buffer'. |
|
4256 |
|
|
4257 |
Candidates are not recomputed, only redisplayed after modifying the |
|
4258 |
whole list of candidates in each source with functions found in |
|
4259 |
`redisplay' attribute of current source. Note that candidates are |
|
4260 |
redisplayed with their display part with all properties included only. |
|
4261 |
This function is used in async sources to transform the whole list of |
|
4262 |
candidates from the sentinel functions (i.e when all candidates have |
|
4263 |
been computed) because other filters like `candidate-transformer' are |
|
4264 |
modifying only each chunk of candidates from process-filter as they |
|
4265 |
come in and not the whole list. Use this for e.g sorting the whole |
|
4266 |
list of async candidates once computed. |
|
4267 |
Note: To ensure redisplay is done in async sources after helm |
|
4268 |
reached `candidate-number-limit' you will have also to redisplay your |
|
4269 |
candidates from `helm-async-outer-limit-hook'." |
|
4270 |
(with-helm-buffer |
|
4271 |
(let ((get-cands (lambda (source) |
|
4272 |
(let ((fns (assoc-default 'redisplay source)) |
|
4273 |
candidates |
|
4274 |
helm-move-to-line-cycle-in-source |
|
4275 |
helm-allow-mouse) |
|
4276 |
(helm-goto-source source) |
|
4277 |
(helm-next-line) |
|
4278 |
(helm-awhile (condition-case-unless-debug nil |
|
4279 |
(and (not (helm-pos-header-line-p)) |
|
4280 |
(helm-get-selection |
|
4281 |
nil 'withprop source)) |
|
4282 |
(error nil)) |
|
4283 |
(push it candidates) |
|
4284 |
(when (save-excursion |
|
4285 |
(forward-line 1) (helm-end-of-source-p t)) |
|
4286 |
(cl-return nil)) |
|
4287 |
(helm-next-line)) |
|
4288 |
(helm-apply-functions-from-source |
|
4289 |
source fns (nreverse candidates))))) |
|
4290 |
(get-sources (lambda () |
|
4291 |
(let (sources helm-move-to-line-cycle-in-source) |
|
4292 |
(helm-awhile (helm-get-current-source) |
|
4293 |
(push it sources) |
|
4294 |
(when (save-excursion |
|
4295 |
(helm-move--end-of-source) |
|
4296 |
(forward-line 1) (eobp)) |
|
4297 |
(cl-return nil)) |
|
4298 |
(helm-next-source)) |
|
4299 |
(nreverse sources))))) |
|
4300 |
(goto-char (point-min)) |
|
4301 |
(helm-update nil (helm-get-current-source) |
|
4302 |
(cl-loop with sources = (funcall get-sources) |
|
4303 |
for s in helm-sources |
|
4304 |
for name = (assoc-default 'name s) collect |
|
4305 |
(when (cl-loop for src in sources thereis |
|
4306 |
(string= name |
|
4307 |
(assoc-default 'name src))) |
|
4308 |
(funcall get-cands s))))))) |
|
4309 |
|
|
4310 |
(defun helm-remove-candidate-cache (source) |
|
4311 |
"Remove SOURCE from `helm-candidate-cache'." |
|
4312 |
(remhash (assoc-default 'name source) helm-candidate-cache)) |
|
4313 |
|
|
4314 |
(defun helm-insert-match (match insert-function &optional num source) |
|
4315 |
"Insert MATCH into `helm-buffer' with INSERT-FUNCTION. |
|
4316 |
If MATCH is a cons cell then insert the car as display with |
|
4317 |
the cdr stored as real value in a `helm-realvalue' text property. |
|
4318 |
Args NUM and SOURCE are also stored as text property when specified as |
|
4319 |
respectively `helm-cand-num' and `helm-cur-source'." |
|
4320 |
(let ((start (point-at-bol (point))) |
|
4321 |
(dispvalue (helm-candidate-get-display match)) |
|
4322 |
(realvalue (cdr-safe match)) |
|
4323 |
(map (when helm-allow-mouse (make-sparse-keymap))) |
|
4324 |
(inhibit-read-only t) |
|
4325 |
end) |
|
4326 |
(when (and (stringp dispvalue) |
|
4327 |
(not (zerop (length dispvalue)))) |
|
4328 |
(funcall insert-function dispvalue) |
|
4329 |
(setq end (point-at-eol)) |
|
4330 |
;; Some strings may handle another keymap prop. |
|
4331 |
(remove-text-properties start end '(keymap nil)) |
|
4332 |
(put-text-property start end 'read-only nil) |
|
4333 |
;; Some sources with candidates-in-buffer have already added |
|
4334 |
;; 'helm-realvalue property when creating candidate buffer. |
|
4335 |
(unless (get-text-property start 'helm-realvalue) |
|
4336 |
(and realvalue |
|
4337 |
(put-text-property start end |
|
4338 |
'helm-realvalue realvalue))) |
|
4339 |
(when map |
|
4340 |
(define-key map [mouse-1] 'helm-mouse-select-candidate) |
|
4341 |
(define-key map [mouse-2] 'ignore) |
|
4342 |
(define-key map [mouse-3] 'helm-select-action) |
|
4343 |
(add-text-properties |
|
4344 |
start end |
|
4345 |
`(mouse-face highlight |
|
4346 |
keymap ,map |
|
4347 |
help-echo ,(pcase (get-text-property start 'help-echo) |
|
4348 |
((and it (pred stringp)) |
|
4349 |
(concat it "\nmouse-1: select candidate\nmouse-3: menu actions")) |
|
4350 |
(_ "mouse-1: select candidate\nmouse-3: menu actions"))))) |
|
4351 |
(when num |
|
4352 |
(put-text-property start end 'helm-cand-num num)) |
|
4353 |
(when source |
|
4354 |
(put-text-property start end 'helm-cur-source source)) |
|
4355 |
(funcall insert-function "\n")))) |
|
4356 |
|
|
4357 |
(defun helm--mouse-reset-selection-help-echo () |
|
4358 |
(let* ((inhibit-read-only t) |
|
4359 |
(start (overlay-start helm-selection-overlay)) |
|
4360 |
(end (overlay-end helm-selection-overlay)) |
|
4361 |
(help-echo (get-text-property start 'help-echo))) |
|
4362 |
(when (and (stringp help-echo) |
|
4363 |
(string-match "mouse-2: execute action" help-echo)) |
|
4364 |
(put-text-property |
|
4365 |
start end |
|
4366 |
'help-echo (replace-match "mouse-1: select candidate" |
|
4367 |
t t help-echo))))) |
|
4368 |
|
|
4369 |
(defun helm--bind-mouse-for-selection (pos) |
|
4370 |
(let ((inhibit-read-only t) |
|
4371 |
(map (get-text-property pos 'keymap))) |
|
4372 |
(when map |
|
4373 |
(define-key map [mouse-2] 'helm-maybe-exit-minibuffer) |
|
4374 |
(put-text-property |
|
4375 |
helm-selection-point |
|
4376 |
(overlay-end helm-selection-overlay) |
|
4377 |
'help-echo (helm-aif (get-text-property pos 'help-echo) |
|
4378 |
(if (and (stringp it) |
|
4379 |
(string-match "mouse-1: select candidate" it)) |
|
4380 |
(replace-match "mouse-2: execute action" t t it) |
|
4381 |
"mouse-2: execute action\nmouse-3: menu actions") |
|
4382 |
"mouse-2: execute action\nmouse-3: menu actions"))))) |
|
4383 |
|
|
4384 |
(defun helm-mouse-select-candidate (event) |
|
4385 |
(interactive "e") |
|
4386 |
(let* ((window (posn-window (event-end event))) |
|
4387 |
(pos (posn-point (event-end event)))) |
|
4388 |
(unwind-protect |
|
4389 |
(with-current-buffer (window-buffer window) |
|
4390 |
(if (and (helm-action-window) |
|
4391 |
(eql window (get-buffer-window helm-buffer))) |
|
4392 |
(user-error "selection in helm-window not available while selecting action") |
|
4393 |
(helm--mouse-reset-selection-help-echo) |
|
4394 |
(goto-char pos) |
|
4395 |
(when (helm-pos-multiline-p) |
|
4396 |
(goto-char (or (helm-get-previous-candidate-separator-pos) |
|
4397 |
(helm-get-previous-header-pos))) |
|
4398 |
(forward-line 1))) |
|
4399 |
(helm-mark-current-line) |
|
4400 |
(helm-follow-execute-persistent-action-maybe)) |
|
4401 |
(select-window (minibuffer-window)) |
|
4402 |
(set-buffer (window-buffer window))))) |
|
4403 |
(put 'helm-mouse-select-candidate 'helm-only t) |
|
4404 |
|
|
4405 |
(defun helm-insert-header-from-source (source) |
|
4406 |
"Insert SOURCE name in `helm-buffer' header. |
|
4407 |
Maybe insert, by overlay, additional info after the source name |
|
4408 |
if SOURCE has header-name attribute." |
|
4409 |
(let ((name (assoc-default 'name source))) |
|
4410 |
(helm-insert-header |
|
4411 |
name |
|
4412 |
(helm-aif (assoc-default 'header-name source) |
|
4413 |
(helm-apply-functions-from-source source it name))))) |
|
4414 |
|
|
4415 |
(defun helm-insert-header (name &optional display-string) |
|
4416 |
"Insert header of source NAME into the helm buffer. |
|
4417 |
If DISPLAY-STRING is non-`nil' and a string value then display |
|
4418 |
this additional info after the source name by overlay." |
|
4419 |
(unless (bobp) |
|
4420 |
(let ((start (point))) |
|
4421 |
(insert "\n") |
|
4422 |
(put-text-property start (point) 'helm-header-separator t))) |
|
4423 |
(let ((start (point))) |
|
4424 |
(insert name) |
|
4425 |
(put-text-property (point-at-bol) |
|
4426 |
(point-at-eol) 'helm-header t) |
|
4427 |
(when display-string |
|
4428 |
(overlay-put (make-overlay (point-at-bol) (point-at-eol)) |
|
4429 |
'display display-string)) |
|
4430 |
(insert "\n") |
|
4431 |
(put-text-property start (point) 'face 'helm-source-header))) |
|
4432 |
|
|
4433 |
(defun helm-insert-candidate-separator () |
|
4434 |
"Insert separator of candidates into the helm buffer." |
|
4435 |
(insert (propertize helm-candidate-separator 'face 'helm-separator)) |
|
4436 |
(put-text-property (point-at-bol) |
|
4437 |
(point-at-eol) 'helm-candidate-separator t) |
|
4438 |
(insert "\n")) |
|
4439 |
|
|
4440 |
|
|
4441 |
;;; Async process |
|
4442 |
;; |
|
4443 |
(defun helm-output-filter (process output-string) |
|
4444 |
"The `process-filter' function for helm async sources." |
|
4445 |
(with-helm-quittable |
|
4446 |
(helm-output-filter-1 (assoc process helm-async-processes) output-string))) |
|
4447 |
|
|
4448 |
(defun helm-output-filter-1 (process-assoc output-string) |
|
4449 |
(helm-log "output-string = %S" output-string) |
|
4450 |
(with-current-buffer helm-buffer |
|
4451 |
(let ((source (cdr process-assoc))) |
|
4452 |
(save-excursion |
|
4453 |
(helm-aif (assoc-default 'insertion-marker source) |
|
4454 |
(goto-char it) |
|
4455 |
(goto-char (point-max)) |
|
4456 |
(helm-insert-header-from-source source) |
|
4457 |
(setcdr process-assoc |
|
4458 |
(append source `((insertion-marker . ,(point-marker)))))) |
|
4459 |
(helm-output-filter--process-source |
|
4460 |
(car process-assoc) output-string source |
|
4461 |
(helm-candidate-number-limit source)))) |
|
4462 |
(helm-output-filter--post-process))) |
|
4463 |
|
|
4464 |
(defun helm-output-filter--process-source (process output-string source limit) |
|
4465 |
(cl-dolist (candidate (helm-transform-candidates |
|
4466 |
(helm-output-filter--collect-candidates |
|
4467 |
(split-string output-string "\n") |
|
4468 |
(assq 'incomplete-line source)) |
|
4469 |
source t)) |
|
4470 |
(setq candidate |
|
4471 |
(helm--maybe-process-filter-one-by-one-candidate candidate source)) |
|
4472 |
(if (assq 'multiline source) |
|
4473 |
(let ((start (point))) |
|
4474 |
(helm-insert-candidate-separator) |
|
4475 |
(helm-insert-match candidate 'insert-before-markers |
|
4476 |
(1+ (cdr (assq 'item-count source))) |
|
4477 |
source) |
|
4478 |
(put-text-property start (point) 'helm-multiline t)) |
|
4479 |
(helm-insert-match candidate 'insert-before-markers |
|
4480 |
(1+ (cdr (assq 'item-count source))) |
|
4481 |
source)) |
|
4482 |
(cl-incf (cdr (assq 'item-count source))) |
|
4483 |
(when (>= (assoc-default 'item-count source) limit) |
|
4484 |
(helm-kill-async-process process) |
|
4485 |
(helm-log-run-hook 'helm-async-outer-limit-hook) |
|
4486 |
(cl-return)))) |
|
4487 |
|
|
4488 |
(defun helm-output-filter--collect-candidates (lines incomplete-line-info) |
|
4489 |
"Collect LINES maybe completing the truncated first and last lines." |
|
4490 |
;; The output of process may come in chunks of any size, so the last |
|
4491 |
;; line of LINES could be truncated, this truncated line is stored |
|
4492 |
;; in INCOMPLETE-LINE-INFO to be concatenated with the first |
|
4493 |
;; incomplete line of the next arriving chunk. INCOMPLETE-LINE-INFO |
|
4494 |
;; is an attribute of source; it is created with an empty string |
|
4495 |
;; when the source is computed => (incomplete-line . "") |
|
4496 |
(helm-log "incomplete-line-info = %S" (cdr incomplete-line-info)) |
|
4497 |
(butlast |
|
4498 |
(cl-loop for line in lines |
|
4499 |
;; On start `incomplete-line-info' value is empty string. |
|
4500 |
for newline = (helm-aif (cdr incomplete-line-info) |
|
4501 |
(prog1 |
|
4502 |
(concat it line) |
|
4503 |
(setcdr incomplete-line-info nil)) |
|
4504 |
line) |
|
4505 |
collect newline |
|
4506 |
;; Store last incomplete line (last chunk truncated) until |
|
4507 |
;; new output arrives. Previously storing 'line' in |
|
4508 |
;; incomplete-line-info assumed output was truncated in |
|
4509 |
;; only two chunks. But output could be large and |
|
4510 |
;; truncated in more than two chunks. Therefore store |
|
4511 |
;; 'newline' to contain the previous chunks (Issue #1187). |
|
4512 |
finally do (setcdr incomplete-line-info newline)))) |
|
4513 |
|
|
4514 |
(defun helm-output-filter--post-process () |
|
4515 |
(helm-aif (get-buffer-window helm-buffer 'visible) |
|
4516 |
(with-selected-window it |
|
4517 |
(helm-skip-noncandidate-line 'next) |
|
4518 |
(helm-mark-current-line nil 'nomouse) |
|
4519 |
;; FIXME Don't hardcode follow delay. |
|
4520 |
(helm-follow-execute-persistent-action-maybe 0.5) |
|
4521 |
(helm-display-mode-line (helm-get-current-source)) |
|
4522 |
(helm-log-run-hook 'helm-after-update-hook) |
|
4523 |
(helm--reset-update-flag)))) |
|
4524 |
|
|
4525 |
(defun helm-process-deferred-sentinel-hook (process event file) |
|
4526 |
"Defer remote processes in sentinels. |
|
4527 |
Meant to be called at the beginning of a sentinel process |
|
4528 |
function." |
|
4529 |
(when (and (not (zerop helm-tramp-connection-min-time-diff)) |
|
4530 |
(string= event "finished\n") |
|
4531 |
(or (file-remote-p file) |
|
4532 |
;; `helm-suspend-update-flag' |
|
4533 |
;; is non-`nil' here only during a |
|
4534 |
;; running process, this will never be called |
|
4535 |
;; when user set it explicitly with `C-!'. |
|
4536 |
helm-suspend-update-flag)) |
|
4537 |
(setq helm-suspend-update-flag t) |
|
4538 |
;; Kill the process but don't delete entry in |
|
4539 |
;; `helm-async-processes'. |
|
4540 |
(helm-kill-async-process process) |
|
4541 |
;; When tramp opens the same connection twice in less than 5 |
|
4542 |
;; seconds, it throws 'suppress, which calls the real-handler on |
|
4543 |
;; the main "Emacs". To avoid this [1] helm waits for 5 seconds |
|
4544 |
;; before updates yet allows user input during this delay. [1] In |
|
4545 |
;; recent Emacs versions, this has been fixed so tramp returns nil |
|
4546 |
;; in such conditions. Note: `tramp-connection-min-time-diff' cannot |
|
4547 |
;; have values less than 5 seconds otherwise the process dies. |
|
4548 |
(run-at-time helm-tramp-connection-min-time-diff |
|
4549 |
nil (lambda () |
|
4550 |
(when helm-alive-p ; Don't run timer fn after quit. |
|
4551 |
(setq helm-suspend-update-flag nil) |
|
4552 |
(helm-check-minibuffer-input)))))) |
|
4553 |
|
|
4554 |
(defun helm-kill-async-processes () |
|
4555 |
"Kill all asynchronous processes registered in `helm-async-processes'." |
|
4556 |
(while helm-async-processes |
|
4557 |
(helm-kill-async-process (caar helm-async-processes)) |
|
4558 |
(setq helm-async-processes (cdr helm-async-processes)))) |
|
4559 |
|
|
4560 |
(defun helm-kill-async-process (process) |
|
4561 |
"Stop output from `helm-output-filter' and kill associated PROCESS." |
|
4562 |
(set-process-filter process nil) |
|
4563 |
(delete-process process)) |
|
4564 |
|
|
4565 |
|
|
4566 |
;;; Actions |
|
4567 |
;; |
|
4568 |
(defun helm-execute-selection-action () |
|
4569 |
"Execute current action." |
|
4570 |
(helm-log-run-hook 'helm-before-action-hook) |
|
4571 |
;; Position can be change when `helm-current-buffer' |
|
4572 |
;; is split, so jump to this position before executing action. |
|
4573 |
(helm-current-position 'restore) |
|
4574 |
(unwind-protect |
|
4575 |
(prog1 (helm-execute-selection-action-1) |
|
4576 |
(helm-log-run-hook 'helm-after-action-hook)) |
|
4577 |
(setq helm--executing-helm-action nil))) |
|
4578 |
|
|
4579 |
(defun helm-execute-selection-action-1 (&optional |
|
4580 |
selection action |
|
4581 |
preserve-saved-action) |
|
4582 |
"Execute ACTION on current SELECTION. |
|
4583 |
If PRESERVE-SAVED-ACTION is non-`nil', then save the action." |
|
4584 |
(helm-log "executing action") |
|
4585 |
(setq action (helm-get-default-action |
|
4586 |
(or action |
|
4587 |
helm-saved-action |
|
4588 |
(if (get-buffer helm-action-buffer) |
|
4589 |
(helm-get-selection helm-action-buffer) |
|
4590 |
(helm-get-actions-from-current-source))))) |
|
4591 |
(helm-aif (and (not helm-in-persistent-action) |
|
4592 |
(get-buffer helm-action-buffer)) |
|
4593 |
(kill-buffer it)) |
|
4594 |
(let ((source (or helm-saved-current-source |
|
4595 |
(helm-get-current-source))) |
|
4596 |
non-essential) |
|
4597 |
(setq selection (helm-coerce-selection |
|
4598 |
(or selection |
|
4599 |
helm-saved-selection |
|
4600 |
(helm-get-selection nil nil source) |
|
4601 |
(and (assq 'accept-empty source) "")) |
|
4602 |
source)) |
|
4603 |
(unless preserve-saved-action (setq helm-saved-action nil)) |
|
4604 |
(when (and selection action) (funcall action selection)))) |
|
4605 |
|
|
4606 |
(defun helm-coerce-selection (selection source) |
|
4607 |
"Apply coerce attribute function to SELECTION in SOURCE. |
|
4608 |
Coerce source with coerce function." |
|
4609 |
(helm-aif (assoc-default 'coerce source) |
|
4610 |
(helm-apply-functions-from-source source it selection) |
|
4611 |
selection)) |
|
4612 |
|
|
4613 |
(defun helm-get-default-action (action) |
|
4614 |
"Get the first ACTION value of action list in source." |
|
4615 |
(if (and (listp action) (not (functionp action))) |
|
4616 |
(cdar action) |
|
4617 |
action)) |
|
4618 |
|
|
4619 |
(defun helm--show-action-window-other-window-p () |
|
4620 |
(and helm-show-action-window-other-window |
|
4621 |
(or helm-always-two-windows |
|
4622 |
helm--buffer-in-new-frame-p) |
|
4623 |
(eq helm-split-window-state 'vertical))) |
|
4624 |
|
|
4625 |
(defun helm-select-action () |
|
4626 |
"Select an action for the currently selected candidate. |
|
4627 |
If action buffer is selected, back to the helm buffer." |
|
4628 |
(interactive) |
|
4629 |
(with-helm-alive-p |
|
4630 |
(let ((src (helm-get-current-source))) |
|
4631 |
(helm-log-run-hook 'helm-select-action-hook) |
|
4632 |
(setq helm-saved-selection (helm-get-selection nil nil src)) |
|
4633 |
(with-selected-frame (with-helm-window (selected-frame)) |
|
4634 |
(prog1 |
|
4635 |
(helm-acond ((get-buffer-window helm-action-buffer 'visible) |
|
4636 |
(set-window-buffer it helm-buffer) |
|
4637 |
(helm--set-action-prompt 'restore) |
|
4638 |
(when (helm--show-action-window-other-window-p) |
|
4639 |
(delete-window it)) |
|
4640 |
(kill-buffer helm-action-buffer) |
|
4641 |
(setq helm-saved-selection nil) |
|
4642 |
(helm-set-pattern helm-input 'noupdate)) |
|
4643 |
(helm-saved-selection |
|
4644 |
(setq helm-saved-current-source src) |
|
4645 |
(let ((actions (helm-get-actions-from-current-source src)) |
|
4646 |
helm-onewindow-p) |
|
4647 |
(if (functionp actions) |
|
4648 |
(message "Sole action: %s" |
|
4649 |
(if (or (consp actions) |
|
4650 |
(byte-code-function-p actions)) |
|
4651 |
"Anonymous" actions)) |
|
4652 |
(helm-show-action-buffer actions) |
|
4653 |
;; Be sure the minibuffer is entirely deleted (#907). |
|
4654 |
(helm--delete-minibuffer-contents-from "") |
|
4655 |
(helm--set-action-prompt) |
|
4656 |
(helm-check-minibuffer-input)))) |
|
4657 |
(t (message "No Actions available"))) |
|
4658 |
(helm-display-mode-line (helm-get-current-source)) |
|
4659 |
(run-hooks 'helm-window-configuration-hook)))))) |
|
4660 |
(put 'helm-select-action 'helm-only t) |
|
4661 |
|
|
4662 |
(defun helm--set-action-prompt (&optional restore) |
|
4663 |
(with-selected-window (minibuffer-window) |
|
4664 |
(let ((inhibit-read-only t) |
|
4665 |
(props '(face minibuffer-prompt |
|
4666 |
field t |
|
4667 |
read-only t |
|
4668 |
rear-nonsticky t |
|
4669 |
front-sticky t)) |
|
4670 |
(prt (if restore helm--prompt helm--action-prompt))) |
|
4671 |
(erase-buffer) |
|
4672 |
(insert (apply #'propertize prt props))))) |
|
4673 |
|
|
4674 |
(defun helm-show-action-buffer (actions) |
|
4675 |
(with-current-buffer (get-buffer-create helm-action-buffer) |
|
4676 |
(erase-buffer) |
|
4677 |
(buffer-disable-undo) |
|
4678 |
(setq cursor-type nil) |
|
4679 |
(set-window-buffer (if (helm--show-action-window-other-window-p) |
|
4680 |
(split-window (get-buffer-window helm-buffer) |
|
4681 |
nil helm-show-action-window-other-window) |
|
4682 |
(get-buffer-window helm-buffer)) |
|
4683 |
helm-action-buffer) |
|
4684 |
(set (make-local-variable 'helm-sources) |
|
4685 |
(list |
|
4686 |
(helm-build-sync-source "Actions" |
|
4687 |
:volatile t |
|
4688 |
:nomark t |
|
4689 |
:persistent-action #'ignore |
|
4690 |
:persistent-help "DoNothing" |
|
4691 |
:keymap 'helm-map |
|
4692 |
:candidates actions |
|
4693 |
:mode-line '("Action(s)" "\\<helm-map>\\[helm-select-action]:BackToCands RET/f1/f2/fn:NthAct") |
|
4694 |
:candidate-transformer |
|
4695 |
(lambda (candidates) |
|
4696 |
(cl-loop for (i . j) in candidates |
|
4697 |
for count from 1 |
|
4698 |
collect |
|
4699 |
(cons (concat (cond ((> count 12) |
|
4700 |
" ") |
|
4701 |
((< count 10) |
|
4702 |
(format "[f%s] " count)) |
|
4703 |
(t (format "[f%s] " count))) |
|
4704 |
(propertize i 'face 'helm-action)) |
|
4705 |
j))) |
|
4706 |
:candidate-number-limit nil))) |
|
4707 |
(set (make-local-variable 'helm-source-filter) nil) |
|
4708 |
(set (make-local-variable 'helm-selection-overlay) nil) |
|
4709 |
(helm-initialize-overlays helm-action-buffer))) |
|
4710 |
|
|
4711 |
|
|
4712 |
;; Selection of candidates |
|
4713 |
|
|
4714 |
(defun helm-display-source-at-screen-top-maybe (unit) |
|
4715 |
"Display source at the top of screen when UNIT value is 'source. |
|
4716 |
Returns nil for any other value of UNIT." |
|
4717 |
(when (and helm-display-source-at-screen-top (eq unit 'source)) |
|
4718 |
(set-window-start (selected-window) |
|
4719 |
(save-excursion (forward-line -1) (point))))) |
|
4720 |
|
|
4721 |
(defun helm-skip-noncandidate-line (direction) |
|
4722 |
"Skip source header or candidates separator when going in DIRECTION. |
|
4723 |
DIRECTION is either 'next or 'previous. |
|
4724 |
Same as `helm-skip-header-and-separator-line' but ensure |
|
4725 |
point is moved to the right place when at bob or eob." |
|
4726 |
(helm-skip-header-and-separator-line direction) |
|
4727 |
(and (bobp) (forward-line 1)) ; Skip first header. |
|
4728 |
(and (eobp) (forward-line -1))) ; Avoid last empty line. |
|
4729 |
|
|
4730 |
(defun helm-skip-header-and-separator-line (direction) |
|
4731 |
"Skip source header or candidate separator when going to next/previous line. |
|
4732 |
DIRECTION is either 'next or 'previous." |
|
4733 |
(let ((fn (cl-ecase direction |
|
4734 |
(next 'eobp) |
|
4735 |
(previous 'bobp)))) |
|
4736 |
(while (and (not (funcall fn)) |
|
4737 |
(or (helm-pos-header-line-p) |
|
4738 |
(helm-pos-candidate-separator-p))) |
|
4739 |
(forward-line (if (and (eq direction 'previous) |
|
4740 |
(not (eq (point-at-bol) (point-min)))) |
|
4741 |
-1 1))))) |
|
4742 |
|
|
4743 |
(defun helm-display-mode-line (source &optional force) |
|
4744 |
"Set up mode line and header line for `helm-buffer'. |
|
4745 |
|
|
4746 |
SOURCE is a Helm source object. |
|
4747 |
|
|
4748 |
Optional argument FORCE forces redisplay of the Helm buffer's |
|
4749 |
mode and header lines." |
|
4750 |
(set (make-local-variable 'helm-mode-line-string) |
|
4751 |
(helm-interpret-value (or (and (listp source) ; Check if source is empty. |
|
4752 |
(assoc-default 'mode-line source)) |
|
4753 |
(default-value 'helm-mode-line-string)) |
|
4754 |
source)) |
|
4755 |
(let ((follow (and (or (helm-follow-mode-p source) |
|
4756 |
(and helm-follow-mode-persistent |
|
4757 |
(member (assoc-default 'name source) |
|
4758 |
helm-source-names-using-follow))) |
|
4759 |
" (HF)")) |
|
4760 |
(marked (and helm-marked-candidates |
|
4761 |
(cl-loop with cur-name = (assoc-default 'name source) |
|
4762 |
for c in helm-marked-candidates |
|
4763 |
for name = (assoc-default 'name (car c)) |
|
4764 |
when (string= name cur-name) |
|
4765 |
collect c)))) |
|
4766 |
;; Setup mode-line. |
|
4767 |
(if helm-mode-line-string |
|
4768 |
(setq mode-line-format |
|
4769 |
`(:propertize |
|
4770 |
(" " mode-line-buffer-identification " " |
|
4771 |
(:eval (format "L%-3d" (helm-candidate-number-at-point))) |
|
4772 |
,follow |
|
4773 |
" " |
|
4774 |
(:eval ,(and marked |
|
4775 |
(propertize |
|
4776 |
(format "M%d" (length marked)) |
|
4777 |
'face 'helm-visible-mark))) |
|
4778 |
(:eval (when ,helm--mode-line-display-prefarg |
|
4779 |
(let ((arg (prefix-numeric-value |
|
4780 |
(or prefix-arg current-prefix-arg)))) |
|
4781 |
(unless (= arg 1) |
|
4782 |
(propertize (format " [prefarg:%s]" arg) |
|
4783 |
'face 'helm-prefarg))))) |
|
4784 |
" " |
|
4785 |
(:eval (with-helm-buffer |
|
4786 |
(helm-show-candidate-number |
|
4787 |
(car-safe helm-mode-line-string)))) |
|
4788 |
" " helm--mode-line-string-real " " |
|
4789 |
(:eval (make-string (window-width) ? ))) |
|
4790 |
keymap (keymap (mode-line keymap |
|
4791 |
(mouse-1 . ignore) |
|
4792 |
(down-mouse-1 . ignore) |
|
4793 |
(drag-mouse-1 . ignore) |
|
4794 |
(mouse-2 . ignore) |
|
4795 |
(down-mouse-2 . ignore) |
|
4796 |
(drag-mouse-2 . ignore) |
|
4797 |
(mouse-3 . ignore) |
|
4798 |
(down-mouse-3 . ignore) |
|
4799 |
(drag-mouse-3 . ignore)))) |
|
4800 |
helm--mode-line-string-real |
|
4801 |
(substitute-command-keys (if (listp helm-mode-line-string) |
|
4802 |
(cadr helm-mode-line-string) |
|
4803 |
helm-mode-line-string))) |
|
4804 |
(setq mode-line-format (default-value 'mode-line-format))) |
|
4805 |
;; Setup header-line. |
|
4806 |
(cond (helm-echo-input-in-header-line |
|
4807 |
(setq force t) |
|
4808 |
(helm--set-header-line)) |
|
4809 |
(helm-display-header-line |
|
4810 |
(let ((hlstr (helm-interpret-value |
|
4811 |
(and (listp source) |
|
4812 |
(assoc-default 'header-line source)) |
|
4813 |
source)) |
|
4814 |
(endstr (make-string (window-width) ? ))) |
|
4815 |
(setq header-line-format |
|
4816 |
(propertize (concat " " hlstr endstr) |
|
4817 |
'face 'helm-header)))))) |
|
4818 |
(when force (force-mode-line-update))) |
|
4819 |
|
|
4820 |
(defun helm--set-header-line (&optional update) |
|
4821 |
(with-selected-window (minibuffer-window) |
|
4822 |
(when helm-display-header-line |
|
4823 |
;; Prevent cursor movement over the overlay displaying |
|
4824 |
;; persistent-help in minibuffer (issue #2108). |
|
4825 |
(setq-local disable-point-adjustment t)) |
|
4826 |
(let* ((beg (save-excursion (vertical-motion 0 (helm-window)) (point))) |
|
4827 |
(end (save-excursion (end-of-visual-line) (point))) |
|
4828 |
;; The visual line where the cursor is. |
|
4829 |
(cont (buffer-substring beg end)) |
|
4830 |
(pref (propertize |
|
4831 |
" " |
|
4832 |
'display (if (string-match-p (regexp-opt `(,helm--prompt |
|
4833 |
,helm--action-prompt)) |
|
4834 |
cont) |
|
4835 |
`(space :width ,helm-header-line-space-before-prompt) |
|
4836 |
(propertize |
|
4837 |
"->" |
|
4838 |
'face 'helm-header-line-left-margin)))) |
|
4839 |
(pos (- (point) beg))) |
|
4840 |
;; Increment pos each time we find a "%" up to current-pos (#1648). |
|
4841 |
(cl-loop for c across (buffer-substring-no-properties beg (point)) |
|
4842 |
when (eql c ?%) do (cl-incf pos)) |
|
4843 |
;; Increment pos when cursor is on a "%" to make it visible in header-line |
|
4844 |
;; i.e "%%|" and not "%|%" (#1649). |
|
4845 |
(when (eql (char-after) ?%) (setq pos (1+ pos))) |
|
4846 |
(setq cont (replace-regexp-in-string "%" "%%" cont)) |
|
4847 |
(with-helm-buffer |
|
4848 |
(setq header-line-format (concat pref cont " ")) |
|
4849 |
(funcall helm-default-prompt-display-function pos) |
|
4850 |
(when update (force-mode-line-update)))))) |
|
4851 |
|
|
4852 |
(defun helm-set-default-prompt-display (pos) |
|
4853 |
(put-text-property |
|
4854 |
;; Increment pos to handle the space before prompt (i.e `pref'). |
|
4855 |
(+ 1 pos) (+ 2 pos) |
|
4856 |
'face |
|
4857 |
;; Don't just use cursor face, this can hide the current character. |
|
4858 |
(list :inverse-video t |
|
4859 |
:foreground (face-background 'cursor) |
|
4860 |
:background (face-background 'default)) |
|
4861 |
header-line-format)) |
|
4862 |
|
|
4863 |
(defun helm-exchange-minibuffer-and-header-line () |
|
4864 |
"Display minibuffer in header-line and vice versa for current helm session. |
|
4865 |
|
|
4866 |
This is a toggle command." |
|
4867 |
(interactive) |
|
4868 |
(with-helm-window |
|
4869 |
(add-hook 'helm-minibuffer-set-up-hook 'helm-hide-minibuffer-maybe) |
|
4870 |
(setq-local helm-echo-input-in-header-line |
|
4871 |
(not helm-echo-input-in-header-line)) |
|
4872 |
(with-selected-window (minibuffer-window) |
|
4873 |
(if (with-helm-buffer helm-echo-input-in-header-line) |
|
4874 |
(helm-hide-minibuffer-maybe) |
|
4875 |
(remove-overlays) |
|
4876 |
(setq cursor-type t))) |
|
4877 |
(helm-display-mode-line (helm-get-current-source) t))) |
|
4878 |
(put 'helm-exchange-minibuffer-and-header-line 'helm-only t) |
|
4879 |
|
|
4880 |
(defun helm--update-header-line () |
|
4881 |
;; This should be used in `post-command-hook', |
|
4882 |
;; nowhere else. |
|
4883 |
(when (with-helm-buffer helm-echo-input-in-header-line) |
|
4884 |
(helm--set-header-line t))) |
|
4885 |
|
|
4886 |
(defun helm-hide-minibuffer-maybe () |
|
4887 |
"Hide minibuffer contents in a Helm session. |
|
4888 |
This function should normally go to `helm-minibuffer-set-up-hook'. |
|
4889 |
It has no effect if `helm-echo-input-in-header-line' is nil." |
|
4890 |
(when (with-helm-buffer helm-echo-input-in-header-line) |
|
4891 |
(let ((ov (make-overlay (point-min) (point-max) nil nil t))) |
|
4892 |
(overlay-put ov 'window (selected-window)) |
|
4893 |
(helm-aif (and helm-display-header-line |
|
4894 |
(helm-attr 'persistent-help)) |
|
4895 |
(progn |
|
4896 |
(overlay-put ov 'display |
|
4897 |
(truncate-string-to-width |
|
4898 |
(substitute-command-keys |
|
4899 |
(concat "\\<helm-map>\\[helm-execute-persistent-action]: " |
|
4900 |
(format "%s (keeping session)" it))) |
|
4901 |
(- (window-width) 1))) |
|
4902 |
(overlay-put ov 'face 'helm-header)) |
|
4903 |
(overlay-put ov 'face (let ((bg-color (face-background 'default nil))) |
|
4904 |
`(:background ,bg-color :foreground ,bg-color)))) |
|
4905 |
|
|
4906 |
(setq cursor-type nil)))) |
|
4907 |
|
|
4908 |
(defun helm-show-candidate-number (&optional name) |
|
4909 |
"Used to display candidate number in mode-line. |
|
4910 |
You can specify NAME of candidates e.g \"Buffers\" otherwise |
|
4911 |
it is \"Candidate\(s\)\" by default." |
|
4912 |
(when helm-alive-p |
|
4913 |
(unless (helm-empty-source-p) |
|
4914 |
;; Build a fixed width string when candidate-number < 1000 |
|
4915 |
(let* ((cand-name (or name "Candidate(s)")) |
|
4916 |
(width (length (format "[999 %s]" cand-name)))) |
|
4917 |
(propertize |
|
4918 |
(format (concat "%-" (number-to-string width) "s") |
|
4919 |
(format "[%s %s]" |
|
4920 |
(helm-get-candidate-number 'in-current-source) |
|
4921 |
cand-name)) |
|
4922 |
'face (if helm-suspend-update-flag |
|
4923 |
'helm-candidate-number-suspended |
|
4924 |
'helm-candidate-number)))))) |
|
4925 |
|
|
4926 |
(cl-defun helm-move-selection-common (&key where direction (follow t)) |
|
4927 |
"Move the selection marker to a new position. |
|
4928 |
Position is determined by WHERE and DIRECTION. |
|
4929 |
Key arg WHERE can be one of: |
|
4930 |
- line |
|
4931 |
- page |
|
4932 |
- edge |
|
4933 |
- source |
|
4934 |
Key arg DIRECTION can be one of: |
|
4935 |
- previous |
|
4936 |
- next |
|
4937 |
- A source or a source name when used with :WHERE 'source." |
|
4938 |
(let ((move-func (cl-case where |
|
4939 |
(line (cl-ecase direction |
|
4940 |
(previous 'helm-move--previous-line-fn) |
|
4941 |
(next 'helm-move--next-line-fn))) |
|
4942 |
(page (cl-ecase direction |
|
4943 |
(previous 'helm-move--previous-page-fn) |
|
4944 |
(next 'helm-move--next-page-fn))) |
|
4945 |
(edge (cl-ecase direction |
|
4946 |
(previous 'helm-move--beginning-of-buffer-fn) |
|
4947 |
(next 'helm-move--end-of-buffer-fn))) |
|
4948 |
(source (cl-case direction |
|
4949 |
(previous 'helm-move--previous-source-fn) |
|
4950 |
(next 'helm-move--next-source-fn) |
|
4951 |
(t (lambda () ; A source is passed as DIRECTION arg. |
|
4952 |
(helm-move--goto-source-fn direction)))))))) |
|
4953 |
(unless (or (helm-empty-buffer-p (helm-buffer-get)) |
|
4954 |
(not (helm-window))) |
|
4955 |
(with-helm-window |
|
4956 |
(when helm-allow-mouse |
|
4957 |
(helm--mouse-reset-selection-help-echo)) |
|
4958 |
(helm-log-run-hook 'helm-move-selection-before-hook) |
|
4959 |
(funcall move-func) |
|
4960 |
(and (memq direction '(next previous)) |
|
4961 |
(helm-skip-noncandidate-line direction)) |
|
4962 |
(when (helm-pos-multiline-p) |
|
4963 |
(helm-move--beginning-of-multiline-candidate)) |
|
4964 |
(helm-display-source-at-screen-top-maybe where) |
|
4965 |
(helm-mark-current-line) |
|
4966 |
(when follow |
|
4967 |
(helm-follow-execute-persistent-action-maybe)) |
|
4968 |
(helm-display-mode-line (helm-get-current-source)) |
|
4969 |
(helm-log-run-hook 'helm-move-selection-after-hook))))) |
|
4970 |
|
|
4971 |
(defun helm-move--beginning-of-multiline-candidate () |
|
4972 |
(let ((header-pos (helm-get-previous-header-pos)) |
|
4973 |
(separator-pos (helm-get-previous-candidate-separator-pos))) |
|
4974 |
(when header-pos |
|
4975 |
(goto-char (if (or (null separator-pos) |
|
4976 |
(< separator-pos header-pos)) |
|
4977 |
header-pos |
|
4978 |
separator-pos)) |
|
4979 |
(forward-line 1)))) |
|
4980 |
|
|
4981 |
(defun helm-move--previous-multi-line-fn () |
|
4982 |
(forward-line -1) |
|
4983 |
(unless (helm-pos-header-line-p) |
|
4984 |
(helm-skip-header-and-separator-line 'previous) |
|
4985 |
(helm-move--beginning-of-multiline-candidate))) |
|
4986 |
|
|
4987 |
(defun helm-move--previous-line-fn () |
|
4988 |
(if (not (helm-pos-multiline-p)) |
|
4989 |
(forward-line -1) |
|
4990 |
(helm-move--previous-multi-line-fn)) |
|
4991 |
(when (and helm-move-to-line-cycle-in-source |
|
4992 |
(helm-pos-header-line-p)) |
|
4993 |
(forward-line 1) |
|
4994 |
(helm-move--end-of-source) |
|
4995 |
;; We are at end of helm-buffer |
|
4996 |
;; check if last candidate is a multiline candidate |
|
4997 |
;; and jump to it |
|
4998 |
(when (and (eobp) |
|
4999 |
(save-excursion (forward-line -1) (helm-pos-multiline-p))) |
|
5000 |
(helm-move--previous-multi-line-fn)))) |
|
5001 |
|
|
5002 |
(defun helm-move--next-multi-line-fn () |
|
5003 |
(let ((header-pos (helm-get-next-header-pos)) |
|
5004 |
(separator-pos (helm-get-next-candidate-separator-pos))) |
|
5005 |
(cond ((and separator-pos |
|
5006 |
(or (null header-pos) (< separator-pos header-pos))) |
|
5007 |
(goto-char separator-pos)) |
|
5008 |
(header-pos |
|
5009 |
(goto-char header-pos))))) |
|
5010 |
|
|
5011 |
(defun helm-move--next-line-fn () |
|
5012 |
(if (not (helm-pos-multiline-p)) |
|
5013 |
(forward-line 1) |
|
5014 |
(helm-move--next-multi-line-fn)) |
|
5015 |
(when (and helm-move-to-line-cycle-in-source |
|
5016 |
(or (save-excursion (and (helm-pos-multiline-p) |
|
5017 |
(goto-char (overlay-end |
|
5018 |
helm-selection-overlay)) |
|
5019 |
(helm-end-of-source-p t))) |
|
5020 |
(helm-end-of-source-p t))) |
|
5021 |
(helm-move--beginning-of-source))) |
|
5022 |
|
|
5023 |
(defun helm-move--previous-page-fn () |
|
5024 |
(condition-case nil |
|
5025 |
(scroll-down) |
|
5026 |
(beginning-of-buffer (goto-char (point-min))))) |
|
5027 |
|
|
5028 |
(defun helm-move--next-page-fn () |
|
5029 |
(condition-case nil |
|
5030 |
(scroll-up) |
|
5031 |
(end-of-buffer (goto-char (point-max))))) |
|
5032 |
|
|
5033 |
(defun helm-move--beginning-of-buffer-fn () |
|
5034 |
(goto-char (point-min))) |
|
5035 |
|
|
5036 |
(defun helm-move--end-of-buffer-fn () |
|
5037 |
(goto-char (point-max))) |
|
5038 |
|
|
5039 |
(defun helm-move--end-of-source () |
|
5040 |
(helm-aif (helm-get-next-header-pos) |
|
5041 |
(progn (goto-char it) (forward-line -2)) |
|
5042 |
(goto-char (point-max)))) |
|
5043 |
|
|
5044 |
(defun helm-move--beginning-of-source () |
|
5045 |
(helm-aif (helm-get-previous-header-pos) |
|
5046 |
(progn (goto-char it) |
|
5047 |
(forward-line 1)) |
|
5048 |
(goto-char (point-min)))) |
|
5049 |
|
|
5050 |
(defun helm-move--previous-source-fn () |
|
5051 |
(forward-line -1) |
|
5052 |
(if (bobp) |
|
5053 |
(goto-char (point-max)) |
|
5054 |
(helm-skip-header-and-separator-line 'previous)) |
|
5055 |
(goto-char (helm-get-previous-header-pos)) |
|
5056 |
(forward-line 1)) |
|
5057 |
|
|
5058 |
(defun helm-move--next-source-fn () |
|
5059 |
(goto-char (or (and (not (save-excursion |
|
5060 |
(forward-line 1) (eobp))) |
|
5061 |
;; Empty source at eob are just |
|
5062 |
;; not displayed unless they are dummy. |
|
5063 |
;; Issue #1117. |
|
5064 |
(helm-get-next-header-pos)) |
|
5065 |
(point-min)))) |
|
5066 |
|
|
5067 |
(defun helm-move--goto-source-fn (source-or-name) |
|
5068 |
(goto-char (point-min)) |
|
5069 |
(let ((name (if (stringp source-or-name) |
|
5070 |
source-or-name |
|
5071 |
(assoc-default 'name source-or-name)))) |
|
5072 |
(if (or (null name) (string= name "")) |
|
5073 |
(forward-line 1) |
|
5074 |
(condition-case err |
|
5075 |
(while (not (string= name (helm-current-line-contents))) |
|
5076 |
(goto-char (helm-get-next-header-pos))) |
|
5077 |
(error (helm-log "%S" err)))))) |
|
5078 |
|
|
5079 |
(defun helm-candidate-number-at-point () |
|
5080 |
(if helm-alive-p |
|
5081 |
(with-helm-buffer |
|
5082 |
(or (get-text-property (point) 'helm-cand-num) 1)) |
|
5083 |
(or (get-text-property (point) 'helm-cand-num) 1))) |
|
5084 |
|
|
5085 |
(defun helm--next-or-previous-line (direction &optional arg) |
|
5086 |
;; Be sure to not use this in non--interactives calls. |
|
5087 |
(let ((helm-move-to-line-cycle-in-source |
|
5088 |
(and helm-move-to-line-cycle-in-source arg))) |
|
5089 |
(if (and arg (> arg 1)) |
|
5090 |
(cl-loop with pos = (helm-candidate-number-at-point) |
|
5091 |
with cand-num = (helm-get-candidate-number t) |
|
5092 |
with iter = (min arg (if (eq direction 'next) |
|
5093 |
(- cand-num pos) |
|
5094 |
(min arg (1- pos)))) |
|
5095 |
for count from 1 |
|
5096 |
while (<= count iter) |
|
5097 |
do |
|
5098 |
(helm-move-selection-common :where 'line :direction direction)) |
|
5099 |
(helm-move-selection-common :where 'line :direction direction)))) |
|
5100 |
|
|
5101 |
(defun helm-previous-line (&optional arg) |
|
5102 |
"Move selection to the ARG previous line(s). |
|
5103 |
Same behavior as `helm-next-line' when called with a numeric prefix arg." |
|
5104 |
(interactive "p") |
|
5105 |
(with-helm-alive-p |
|
5106 |
(helm--next-or-previous-line 'previous arg))) |
|
5107 |
(put 'helm-previous-line 'helm-only t) |
|
5108 |
|
|
5109 |
(defun helm-next-line (&optional arg) |
|
5110 |
"Move selection to the next ARG line(s). |
|
5111 |
When numeric prefix arg is > than the number of candidates, then |
|
5112 |
move to the last candidate of current source (i.e. don't move to |
|
5113 |
next source)." |
|
5114 |
(interactive "p") |
|
5115 |
(with-helm-alive-p |
|
5116 |
(helm--next-or-previous-line 'next arg))) |
|
5117 |
(put 'helm-next-line 'helm-only t) |
|
5118 |
|
|
5119 |
(defun helm-previous-page () |
|
5120 |
"Move selection back with a pageful." |
|
5121 |
(interactive) |
|
5122 |
(with-helm-alive-p |
|
5123 |
(helm-move-selection-common :where 'page :direction 'previous))) |
|
5124 |
(put 'helm-previous-page 'helm-only t) |
|
5125 |
|
|
5126 |
(defun helm-next-page () |
|
5127 |
"Move selection forward with a pageful." |
|
5128 |
(interactive) |
|
5129 |
(with-helm-alive-p |
|
5130 |
(helm-move-selection-common :where 'page :direction 'next))) |
|
5131 |
(put 'helm-next-page 'helm-only t) |
|
5132 |
|
|
5133 |
(defun helm-beginning-of-buffer () |
|
5134 |
"Move selection at the top." |
|
5135 |
(interactive) |
|
5136 |
(with-helm-alive-p |
|
5137 |
(helm-move-selection-common :where 'edge :direction 'previous))) |
|
5138 |
(put 'helm-beginning-of-buffer 'helm-only t) |
|
5139 |
|
|
5140 |
(defun helm-end-of-buffer () |
|
5141 |
"Move selection at the bottom." |
|
5142 |
(interactive) |
|
5143 |
(with-helm-alive-p |
|
5144 |
(helm-move-selection-common :where 'edge :direction 'next))) |
|
5145 |
(put 'helm-end-of-buffer 'helm-only t) |
|
5146 |
|
|
5147 |
(defun helm-previous-source () |
|
5148 |
"Move selection to the previous source." |
|
5149 |
(interactive) |
|
5150 |
(with-helm-alive-p |
|
5151 |
(helm-move-selection-common :where 'source :direction 'previous))) |
|
5152 |
(put 'helm-previous-source 'helm-only t) |
|
5153 |
|
|
5154 |
(defun helm-next-source () |
|
5155 |
"Move selection to the next source." |
|
5156 |
(interactive) |
|
5157 |
(with-helm-alive-p |
|
5158 |
(helm-move-selection-common :where 'source :direction 'next))) |
|
5159 |
(put 'helm-next-source 'helm-only t) |
|
5160 |
|
|
5161 |
(defun helm-goto-source (&optional source-or-name) |
|
5162 |
"Move the selection to the source named SOURCE-OR-NAME. |
|
5163 |
|
|
5164 |
If SOURCE-OR-NAME is empty string or nil go to the first candidate of |
|
5165 |
first source." |
|
5166 |
(helm-move-selection-common :where 'source :direction source-or-name)) |
|
5167 |
|
|
5168 |
(defun helm--follow-action (arg) |
|
5169 |
(let ((helm--temp-follow-flag t) ; Needed in HFF. |
|
5170 |
(in-follow-mode (helm-follow-mode-p))) |
|
5171 |
;; When follow-mode is already enabled, just go to next or |
|
5172 |
;; previous line. |
|
5173 |
(when (or (eq last-command 'helm-follow-action-forward) |
|
5174 |
(eq last-command 'helm-follow-action-backward) |
|
5175 |
(eq last-command 'helm-execute-persistent-action) |
|
5176 |
in-follow-mode) |
|
5177 |
(if (> arg 0) |
|
5178 |
(helm-move-selection-common :where 'line |
|
5179 |
:direction 'next |
|
5180 |
:follow nil) |
|
5181 |
(helm-move-selection-common :where 'line |
|
5182 |
:direction 'previous |
|
5183 |
:follow nil))) |
|
5184 |
(unless in-follow-mode |
|
5185 |
(helm-execute-persistent-action)))) |
|
5186 |
|
|
5187 |
(defun helm-follow-action-forward () |
|
5188 |
"Go to next line and execute persistent action." |
|
5189 |
(interactive) |
|
5190 |
(with-helm-alive-p (helm--follow-action 1))) |
|
5191 |
(put 'helm-follow-action-forward 'helm-only t) |
|
5192 |
|
|
5193 |
(defun helm-follow-action-backward () |
|
5194 |
"Go to previous line and execute persistent action." |
|
5195 |
(interactive) |
|
5196 |
(with-helm-alive-p (helm--follow-action -1))) |
|
5197 |
(put 'helm-follow-action-backward 'helm-only t) |
|
5198 |
|
|
5199 |
(defun helm-mark-current-line (&optional resumep nomouse) |
|
5200 |
"Move `helm-selection-overlay' to current line. |
|
5201 |
When RESUMEP is non nil move overlay to `helm-selection-point'. |
|
5202 |
When NOMOUSE is specified do not set mouse bindings. |
|
5203 |
|
|
5204 |
Note that selection is unrelated to visible marks used for marking |
|
5205 |
candidates." |
|
5206 |
(with-helm-buffer |
|
5207 |
(when resumep |
|
5208 |
(goto-char helm-selection-point)) |
|
5209 |
(move-overlay |
|
5210 |
helm-selection-overlay (point-at-bol) |
|
5211 |
(if (helm-pos-multiline-p) |
|
5212 |
(let ((header-pos (helm-get-next-header-pos)) |
|
5213 |
(separator-pos (helm-get-next-candidate-separator-pos))) |
|
5214 |
(or (and (null header-pos) separator-pos) |
|
5215 |
(and header-pos separator-pos |
|
5216 |
(< separator-pos header-pos) |
|
5217 |
separator-pos) |
|
5218 |
header-pos |
|
5219 |
(point-max))) |
|
5220 |
(1+ (point-at-eol)))) |
|
5221 |
(setq helm-selection-point (overlay-start helm-selection-overlay)) |
|
5222 |
(when (and helm-allow-mouse (null nomouse)) |
|
5223 |
(helm--bind-mouse-for-selection helm-selection-point)))) |
|
5224 |
|
|
5225 |
(defun helm-confirm-and-exit-minibuffer () |
|
5226 |
"Maybe ask for confirmation when exiting helm. |
|
5227 |
It is similar to `minibuffer-complete-and-exit' adapted to helm. |
|
5228 |
If `minibuffer-completion-confirm' value is 'confirm, |
|
5229 |
send minibuffer confirm message and exit on next hit. |
|
5230 |
If `minibuffer-completion-confirm' value is t, |
|
5231 |
don't exit and send message 'no match'." |
|
5232 |
(interactive) |
|
5233 |
(with-helm-alive-p |
|
5234 |
(if (and (helm--updating-p) |
|
5235 |
(null helm--reading-passwd-or-string)) |
|
5236 |
(progn (message "[Display not ready]") |
|
5237 |
(sit-for 0.5) (message nil)) |
|
5238 |
(let* ((src (helm-get-current-source)) |
|
5239 |
(empty-buffer-p (with-current-buffer helm-buffer |
|
5240 |
(eq (point-min) (point-max)))) |
|
5241 |
(sel (helm-get-selection nil nil src)) |
|
5242 |
(unknown (and (not empty-buffer-p) |
|
5243 |
(string= (get-text-property |
|
5244 |
0 'display |
|
5245 |
(helm-get-selection nil 'withprop src)) |
|
5246 |
"[?]")))) |
|
5247 |
(cond ((and (or empty-buffer-p unknown) |
|
5248 |
(eq minibuffer-completion-confirm 'confirm)) |
|
5249 |
(setq helm-minibuffer-confirm-state |
|
5250 |
'confirm) |
|
5251 |
(setq minibuffer-completion-confirm nil) |
|
5252 |
(minibuffer-message " [confirm]")) |
|
5253 |
((and (or empty-buffer-p |
|
5254 |
(unless (if minibuffer-completing-file-name |
|
5255 |
(and minibuffer-completion-predicate |
|
5256 |
(funcall minibuffer-completion-predicate sel)) |
|
5257 |
(and (stringp sel) |
|
5258 |
;; SEL may be a cons cell when helm-comp-read |
|
5259 |
;; is called directly with a collection composed |
|
5260 |
;; of (display . real) and real is a cons cell. |
|
5261 |
(try-completion sel minibuffer-completion-table |
|
5262 |
minibuffer-completion-predicate))) |
|
5263 |
unknown)) |
|
5264 |
(eq minibuffer-completion-confirm t)) |
|
5265 |
(minibuffer-message " [No match]")) |
|
5266 |
(t |
|
5267 |
(setq helm-minibuffer-confirm-state nil) |
|
5268 |
(helm-exit-minibuffer))))))) |
|
5269 |
(put 'helm-confirm-and-exit-minibuffer 'helm-only t) |
|
5270 |
|
|
5271 |
(add-hook 'helm-after-update-hook 'helm-confirm-and-exit-hook) |
|
5272 |
|
|
5273 |
(defun helm-confirm-and-exit-hook () |
|
5274 |
"Restore `minibuffer-completion-confirm' when helm update." |
|
5275 |
(unless (or (eq minibuffer-completion-confirm t) |
|
5276 |
(not helm-minibuffer-confirm-state)) |
|
5277 |
(setq minibuffer-completion-confirm |
|
5278 |
helm-minibuffer-confirm-state))) |
|
5279 |
|
|
5280 |
(defun helm-read-string (prompt &optional initial-input history |
|
5281 |
default-value inherit-input-method) |
|
5282 |
"Same as `read-string' but for reading string from a helm session." |
|
5283 |
(let ((helm--reading-passwd-or-string t)) |
|
5284 |
(read-string |
|
5285 |
prompt initial-input history default-value inherit-input-method))) |
|
5286 |
|
|
5287 |
(defun helm--updating-p () |
|
5288 |
;; helm timer is between two cycles. |
|
5289 |
;; IOW `helm-check-minibuffer-input' haven't yet compared input |
|
5290 |
;; and `helm-pattern'. |
|
5291 |
(or (not (equal (minibuffer-contents) helm-pattern)) |
|
5292 |
;; `helm-check-minibuffer-input' have launched `helm-update'. |
|
5293 |
helm--in-update)) |
|
5294 |
|
|
5295 |
(defun helm-maybe-exit-minibuffer () |
|
5296 |
(interactive) |
|
5297 |
(with-helm-alive-p |
|
5298 |
(if (and (helm--updating-p) |
|
5299 |
(null helm--reading-passwd-or-string)) |
|
5300 |
(progn |
|
5301 |
(message "[Display not ready]") |
|
5302 |
(sit-for 0.5) (message nil) |
|
5303 |
(helm-update)) |
|
5304 |
(helm-exit-minibuffer)))) |
|
5305 |
(put 'helm-maybe-exit-minibuffer 'helm-only t) |
|
5306 |
|
|
5307 |
(defun helm-exit-minibuffer () |
|
5308 |
"Select the current candidate by exiting the minibuffer." |
|
5309 |
(unless helm-current-prefix-arg |
|
5310 |
(setq helm-current-prefix-arg current-prefix-arg)) |
|
5311 |
(setq helm-exit-status 0) |
|
5312 |
(helm-log-run-hook 'helm-exit-minibuffer-hook) |
|
5313 |
(exit-minibuffer)) |
|
5314 |
|
|
5315 |
(defun helm-keyboard-quit () |
|
5316 |
"Quit minibuffer in helm. |
|
5317 |
If action buffer is displayed, kill it." |
|
5318 |
(interactive) |
|
5319 |
(with-helm-alive-p |
|
5320 |
(when (get-buffer-window helm-action-buffer 'visible) |
|
5321 |
(kill-buffer helm-action-buffer)) |
|
5322 |
(setq helm-exit-status 1) |
|
5323 |
(abort-recursive-edit))) |
|
5324 |
(put 'helm-keyboard-quit 'helm-only t) |
|
5325 |
|
|
5326 |
(defun helm-get-next-header-pos () |
|
5327 |
"Return the position of the next header from point." |
|
5328 |
(next-single-property-change (point) 'helm-header)) |
|
5329 |
|
|
5330 |
(defun helm-get-previous-header-pos () |
|
5331 |
"Return the position of the previous header from point." |
|
5332 |
(previous-single-property-change (point) 'helm-header)) |
|
5333 |
|
|
5334 |
(defun helm-pos-multiline-p () |
|
5335 |
"Return non-`nil' if the current position is in the multiline source region." |
|
5336 |
(get-text-property (point) 'helm-multiline)) |
|
5337 |
|
|
5338 |
(defun helm-get-next-candidate-separator-pos () |
|
5339 |
"Return the position of the next candidate separator from point." |
|
5340 |
(let ((hp (helm-get-next-header-pos))) |
|
5341 |
(helm-aif (next-single-property-change (point) 'helm-candidate-separator) |
|
5342 |
(or |
|
5343 |
;; Be sure we don't catch |
|
5344 |
;; the separator of next source. |
|
5345 |
(and hp (< it hp) it) |
|
5346 |
;; The separator found is in next source |
|
5347 |
;; we are at last cand, so use the header pos. |
|
5348 |
(and hp (< hp it) hp) |
|
5349 |
;; A single source, just try next separator. |
|
5350 |
it)))) |
|
5351 |
|
|
5352 |
(defun helm-get-previous-candidate-separator-pos () |
|
5353 |
"Return the position of the previous candidate separator from point." |
|
5354 |
(previous-single-property-change (point) 'helm-candidate-separator)) |
|
5355 |
|
|
5356 |
(defun helm-pos-header-line-p () |
|
5357 |
"Return t if the current line is a header line." |
|
5358 |
(or (get-text-property (point-at-bol) 'helm-header) |
|
5359 |
(get-text-property (point-at-bol) 'helm-header-separator))) |
|
5360 |
|
|
5361 |
(defun helm-pos-candidate-separator-p () |
|
5362 |
"Return t if the current line is a candidate separator." |
|
5363 |
(get-text-property (point-at-bol) 'helm-candidate-separator)) |
|
5364 |
|
|
5365 |
|
|
5366 |
;;; Debugging |
|
5367 |
;; |
|
5368 |
;; |
|
5369 |
(defun helm-debug-output () |
|
5370 |
"Show all helm-related variables at this time." |
|
5371 |
(interactive) |
|
5372 |
(with-helm-alive-p |
|
5373 |
(helm-help-internal " *Helm Debug*" 'helm-debug-output-function))) |
|
5374 |
(put 'helm-debug-output 'helm-only t) |
|
5375 |
|
|
5376 |
(defun helm-debug-output-function (&optional vars) |
|
5377 |
(message "Calculating all helm-related values...") |
|
5378 |
(insert "If you debug some variables or forms, set `helm-debug-variables' |
|
5379 |
to a list of forms.\n\n") |
|
5380 |
(cl-dolist (v (or vars |
|
5381 |
helm-debug-variables |
|
5382 |
(apropos-internal "^helm-" 'boundp))) |
|
5383 |
(insert "** " |
|
5384 |
(pp-to-string v) "\n" |
|
5385 |
(pp-to-string (with-current-buffer helm-buffer (eval v))) "\n")) |
|
5386 |
(message "Calculating all helm-related values...Done")) |
|
5387 |
|
|
5388 |
(defun helm-enable-or-switch-to-debug () |
|
5389 |
"First hit enable helm debugging, second hit switch to debug buffer." |
|
5390 |
(interactive) |
|
5391 |
(with-helm-alive-p |
|
5392 |
(if helm-debug |
|
5393 |
(helm-run-after-exit |
|
5394 |
#'helm-debug-open-last-log) |
|
5395 |
(setq helm-debug t) |
|
5396 |
(with-helm-buffer (setq truncate-lines nil)) |
|
5397 |
(message "Debugging enabled")))) |
|
5398 |
(put 'helm-enable-or-switch-to-debug 'helm-only t) |
|
5399 |
|
|
5400 |
|
|
5401 |
;; Misc |
|
5402 |
(defun helm-kill-buffer-hook () |
|
5403 |
"Remove tick entry from `helm-tick-hash' and remove buffer from |
|
5404 |
`helm-buffers' when killing a buffer." |
|
5405 |
(cl-loop for key being the hash-keys in helm-tick-hash |
|
5406 |
if (string-match (format "^%s/" (regexp-quote (buffer-name))) key) |
|
5407 |
do (remhash key helm-tick-hash)) |
|
5408 |
(setq helm-buffers (remove (buffer-name) helm-buffers))) |
|
5409 |
(add-hook 'kill-buffer-hook 'helm-kill-buffer-hook) |
|
5410 |
|
|
5411 |
(defun helm-preselect (candidate-or-regexp &optional source) |
|
5412 |
"Move selection to CANDIDATE-OR-REGEXP on Helm start. |
|
5413 |
|
|
5414 |
CANDIDATE-OR-REGEXP can be a: |
|
5415 |
|
|
5416 |
- String |
|
5417 |
- Cons cell of two strings |
|
5418 |
- Nullary function, which moves to a candidate |
|
5419 |
|
|
5420 |
When CANDIDATE-OR-REGEXP is a cons cell, tries moving to first |
|
5421 |
element of the cons cell, then the second, and so on. This allows |
|
5422 |
selection of duplicate candidates after the first. |
|
5423 |
|
|
5424 |
Optional argument SOURCE is a Helm source object." |
|
5425 |
(with-helm-buffer |
|
5426 |
(when candidate-or-regexp |
|
5427 |
(if source |
|
5428 |
(helm-goto-source source) |
|
5429 |
(goto-char (point-min)) |
|
5430 |
(forward-line 1)) |
|
5431 |
(if (functionp candidate-or-regexp) |
|
5432 |
(funcall candidate-or-regexp) |
|
5433 |
(let ((start (point)) mp) |
|
5434 |
(helm-awhile (if (consp candidate-or-regexp) |
|
5435 |
(and (re-search-forward (car candidate-or-regexp) nil t) |
|
5436 |
(re-search-forward (cdr candidate-or-regexp) nil t)) |
|
5437 |
(re-search-forward candidate-or-regexp nil t)) |
|
5438 |
;; If search fall on an header line continue loop |
|
5439 |
;; until it match or fail (Issue #1509). |
|
5440 |
(unless (helm-pos-header-line-p) (cl-return (setq mp it)))) |
|
5441 |
(goto-char (or mp start))))) |
|
5442 |
(forward-line 0) ; Avoid scrolling right on long lines. |
|
5443 |
(when (helm-pos-multiline-p) |
|
5444 |
(helm-move--beginning-of-multiline-candidate)) |
|
5445 |
(when (helm-pos-header-line-p) (forward-line 1)) |
|
5446 |
(when helm-allow-mouse |
|
5447 |
(helm--mouse-reset-selection-help-echo)) |
|
5448 |
(helm-mark-current-line) |
|
5449 |
(helm-display-mode-line (or source (helm-get-current-source))) |
|
5450 |
(helm-log-run-hook 'helm-after-preselection-hook))) |
|
5451 |
|
|
5452 |
(defun helm-delete-current-selection () |
|
5453 |
"Delete the currently selected item." |
|
5454 |
(with-helm-window |
|
5455 |
(cond ((helm-pos-multiline-p) |
|
5456 |
(helm-aif (helm-get-next-candidate-separator-pos) |
|
5457 |
(delete-region (point-at-bol) |
|
5458 |
(1+ (progn (goto-char it) (point-at-eol)))) |
|
5459 |
;; last candidate |
|
5460 |
(goto-char (helm-get-previous-candidate-separator-pos)) |
|
5461 |
(delete-region (point-at-bol) (point-max))) |
|
5462 |
(when (helm-end-of-source-p) |
|
5463 |
(goto-char (or (helm-get-previous-candidate-separator-pos) |
|
5464 |
(point-min))) |
|
5465 |
(forward-line 1))) |
|
5466 |
(t |
|
5467 |
(delete-region (point-at-bol) (1+ (point-at-eol))) |
|
5468 |
(when (helm-end-of-source-p t) |
|
5469 |
(let ((headp (save-excursion |
|
5470 |
(forward-line -1) |
|
5471 |
(not (helm-pos-header-line-p))))) |
|
5472 |
(and headp (forward-line -1)))))) |
|
5473 |
(unless (helm-end-of-source-p t) |
|
5474 |
(helm-mark-current-line)))) |
|
5475 |
|
|
5476 |
(defun helm-end-of-source-1 (n at-point) |
|
5477 |
(save-excursion |
|
5478 |
(if (and (helm-pos-multiline-p) (null at-point)) |
|
5479 |
(null (helm-get-next-candidate-separator-pos)) |
|
5480 |
(forward-line (if at-point 0 n)) |
|
5481 |
(or (eq (point-at-bol) (point-at-eol)) |
|
5482 |
(helm-pos-header-line-p) |
|
5483 |
(if (< n 0) (bobp) (eobp)))))) |
|
5484 |
|
|
5485 |
(defun helm-end-of-source-p (&optional at-point) |
|
5486 |
"Return non-`nil' if we are at eob or end of source." |
|
5487 |
(helm-end-of-source-1 1 at-point)) |
|
5488 |
|
|
5489 |
(defun helm-beginning-of-source-p (&optional at-point) |
|
5490 |
"Return non-`nil' if we are at bob or beginning of source." |
|
5491 |
(helm-end-of-source-1 -1 at-point)) |
|
5492 |
|
|
5493 |
(defun helm--edit-current-selection-internal (func) |
|
5494 |
(with-helm-window |
|
5495 |
(forward-line 0) |
|
5496 |
(let ((realvalue (get-text-property (point) 'helm-realvalue)) |
|
5497 |
(multiline (get-text-property (point) 'helm-multiline))) |
|
5498 |
(funcall func) |
|
5499 |
(forward-line 0) |
|
5500 |
(and realvalue |
|
5501 |
(put-text-property (point) (point-at-eol) |
|
5502 |
'helm-realvalue realvalue)) |
|
5503 |
(and multiline |
|
5504 |
(put-text-property (point) |
|
5505 |
(or (helm-get-next-candidate-separator-pos) |
|
5506 |
(point-max)) |
|
5507 |
'helm-multiline multiline)) |
|
5508 |
(helm-mark-current-line)))) |
|
5509 |
|
|
5510 |
(defmacro helm-edit-current-selection (&rest forms) |
|
5511 |
"Evaluate FORMS at current selection in the helm buffer. |
|
5512 |
Used generally to modify current selection." |
|
5513 |
(declare (indent 0) (debug t)) |
|
5514 |
`(helm--edit-current-selection-internal |
|
5515 |
(lambda () ,@forms))) |
|
5516 |
|
|
5517 |
(defun helm--delete-minibuffer-contents-from (from-str) |
|
5518 |
;; Giving an empty string value to FROM-STR delete all. |
|
5519 |
(let ((input (minibuffer-contents))) |
|
5520 |
(helm-reset-yank-point) |
|
5521 |
(if (> (length input) 0) |
|
5522 |
;; minibuffer is not empty, delete contents from end |
|
5523 |
;; of FROM-STR and update. |
|
5524 |
(helm-set-pattern from-str) |
|
5525 |
;; minibuffer is already empty, force update. |
|
5526 |
(helm-force-update)))) |
|
5527 |
|
|
5528 |
(defun helm-delete-minibuffer-contents (&optional arg) |
|
5529 |
"Delete minibuffer contents. |
|
5530 |
When `helm-delete-minibuffer-contents-from-point' is non-`nil', |
|
5531 |
delete minibuffer contents from point instead of deleting all. |
|
5532 |
Giving a prefix arg reverses this behavior. |
|
5533 |
When at the end of minibuffer, deletes all." |
|
5534 |
(interactive "P") |
|
5535 |
(let ((str (if helm-delete-minibuffer-contents-from-point |
|
5536 |
(if (or arg (eobp)) |
|
5537 |
"" (helm-minibuffer-completion-contents)) |
|
5538 |
(if (and arg (not (eobp))) |
|
5539 |
(helm-minibuffer-completion-contents) "")))) |
|
5540 |
(helm--delete-minibuffer-contents-from str))) |
|
5541 |
|
|
5542 |
|
|
5543 |
;;; helm-source-in-buffer. |
|
5544 |
;; |
|
5545 |
(defun helm-candidates-in-buffer (&optional source) |
|
5546 |
"The top level function used to store candidates with `helm-source-in-buffer'. |
|
5547 |
|
|
5548 |
Candidates are stored in a buffer generated internally |
|
5549 |
by `helm-candidate-buffer' function. |
|
5550 |
Each candidate must be placed in one line. |
|
5551 |
|
|
5552 |
The buffer is created and fed in the init attribute function of helm. |
|
5553 |
|
|
5554 |
e.g: |
|
5555 |
|
|
5556 |
(helm-build-in-buffer-source \"test\" |
|
5557 |
:init (lambda () |
|
5558 |
(helm-init-candidates-in-buffer |
|
5559 |
'global '(foo foa fob bar baz)))) |
|
5560 |
|
|
5561 |
A shortcut can be used to simplify: |
|
5562 |
|
|
5563 |
(helm-build-in-buffer-source \"test\" |
|
5564 |
:data '(foo foa fob bar baz)) |
|
5565 |
|
|
5566 |
By default, `helm' makes candidates by evaluating the |
|
5567 |
candidates function, then narrows them by `string-match' for each |
|
5568 |
candidate. |
|
5569 |
|
|
5570 |
But this is slow for large number of candidates. The new way is |
|
5571 |
to store all candidates in a buffer and then narrow |
|
5572 |
with `re-search-forward'. Search function is customizable by search |
|
5573 |
attribute. The important point is that buffer processing is MUCH |
|
5574 |
FASTER than string list processing and is the Emacs way. |
|
5575 |
|
|
5576 |
The init function writes all candidates to a newly-created |
|
5577 |
candidate buffer. The candidates buffer is created or specified |
|
5578 |
by `helm-candidate-buffer'. Candidates are stored in a line. |
|
5579 |
|
|
5580 |
The candidates function narrows all candidates, IOW creates a |
|
5581 |
subset of candidates dynamically. |
|
5582 |
|
|
5583 |
Class `helm-source-in-buffer' is implemented with three attributes: |
|
5584 |
|
|
5585 |
(candidates . helm-candidates-in-buffer) |
|
5586 |
(volatile) |
|
5587 |
(match identity) |
|
5588 |
|
|
5589 |
The volatile attribute is needed because `helm-candidates-in-buffer' |
|
5590 |
creates candidates dynamically and need to be called every |
|
5591 |
time `helm-pattern' changes. |
|
5592 |
|
|
5593 |
Because `helm-candidates-in-buffer' plays the role of `match' attribute |
|
5594 |
function, specifying `(match identity)' makes the source slightly faster. |
|
5595 |
|
|
5596 |
However if source contains `match-part' attribute, match is computed only |
|
5597 |
on part of candidate returned by the call of function provided by this attribute. |
|
5598 |
The function should have one arg, candidate, and return only |
|
5599 |
a specific part of candidate. |
|
5600 |
|
|
5601 |
To customize `helm-candidates-in-buffer' behavior, |
|
5602 |
use `search', `get-line' and `match-part' attributes." |
|
5603 |
(let ((src (or source (helm-get-current-source)))) |
|
5604 |
(helm-candidates-in-buffer-1 |
|
5605 |
(helm-candidate-buffer) |
|
5606 |
helm-pattern |
|
5607 |
(or (assoc-default 'get-line src) |
|
5608 |
#'buffer-substring-no-properties) |
|
5609 |
(or (assoc-default 'search src) |
|
5610 |
'(helm-candidates-in-buffer-search-default-fn)) |
|
5611 |
(helm-candidate-number-limit src) |
|
5612 |
(helm-attr 'match-part) |
|
5613 |
src))) |
|
5614 |
|
|
5615 |
(defun helm-candidates-in-buffer-search-default-fn (pattern) |
|
5616 |
"Search PATTERN with `re-search-forward' with bound and noerror args." |
|
5617 |
(condition-case _err |
|
5618 |
(re-search-forward pattern nil t) |
|
5619 |
(invalid-regexp nil))) |
|
5620 |
|
|
5621 |
(defun helm-candidates-in-buffer-1 (buffer pattern get-line-fn |
|
5622 |
search-fns limit |
|
5623 |
match-part-fn source) |
|
5624 |
"Return the list of candidates inserted in BUFFER matching PATTERN." |
|
5625 |
;; buffer == nil when candidates buffer does not exist. |
|
5626 |
(when buffer |
|
5627 |
(with-current-buffer buffer |
|
5628 |
(let ((inhibit-point-motion-hooks t) |
|
5629 |
(start-point (1- (point-min)))) |
|
5630 |
(goto-char start-point) |
|
5631 |
(if (string= pattern "") |
|
5632 |
(helm-initial-candidates-from-candidate-buffer |
|
5633 |
get-line-fn limit) |
|
5634 |
(helm-search-from-candidate-buffer |
|
5635 |
pattern get-line-fn search-fns limit |
|
5636 |
start-point match-part-fn source)))))) |
|
5637 |
|
|
5638 |
|
|
5639 |
(defun helm-search-from-candidate-buffer (pattern get-line-fn search-fns |
|
5640 |
limit start-point match-part-fn source) |
|
5641 |
(let ((inhibit-read-only t)) |
|
5642 |
(helm--search-from-candidate-buffer-1 |
|
5643 |
(lambda () |
|
5644 |
(cl-loop with hash = (make-hash-table :test 'equal) |
|
5645 |
with allow-dups = (assq 'allow-dups source) |
|
5646 |
with case-fold-search = (helm-set-case-fold-search) |
|
5647 |
with count = 0 |
|
5648 |
for iter from 1 |
|
5649 |
for searcher in search-fns |
|
5650 |
do (progn |
|
5651 |
(goto-char start-point) |
|
5652 |
;; The character at start-point is a newline, |
|
5653 |
;; if pattern match it that's mean we are |
|
5654 |
;; searching for newline in buffer, in this |
|
5655 |
;; case skip this false line. |
|
5656 |
;; See comment >>>[1] in |
|
5657 |
;; `helm--search-from-candidate-buffer-1'. |
|
5658 |
(and (condition-case nil |
|
5659 |
(looking-at pattern) |
|
5660 |
(invalid-regexp nil)) |
|
5661 |
(forward-line 1))) |
|
5662 |
nconc |
|
5663 |
(cl-loop with pos-lst |
|
5664 |
while (and (setq pos-lst (funcall searcher pattern)) |
|
5665 |
(not (eobp)) |
|
5666 |
(< count limit)) |
|
5667 |
for cand = (apply get-line-fn |
|
5668 |
(if (and pos-lst (listp pos-lst)) |
|
5669 |
pos-lst |
|
5670 |
(list (point-at-bol) (point-at-eol)))) |
|
5671 |
when (and match-part-fn |
|
5672 |
(not (get-text-property 0 'match-part cand))) |
|
5673 |
do (setq cand |
|
5674 |
(propertize cand 'match-part (funcall match-part-fn cand))) |
|
5675 |
for dup = (gethash cand hash) |
|
5676 |
when (and (or (and allow-dups dup (= dup iter)) |
|
5677 |
(null dup)) |
|
5678 |
(or |
|
5679 |
;; Always collect when cand is matched |
|
5680 |
;; by searcher funcs and match-part attr |
|
5681 |
;; is not present. |
|
5682 |
(and (not match-part-fn) |
|
5683 |
(not (consp pos-lst))) |
|
5684 |
;; If match-part attr is present, or if SEARCHER fn |
|
5685 |
;; returns a cons cell, collect PATTERN only if it |
|
5686 |
;; match the part of CAND specified by |
|
5687 |
;; the match-part func. |
|
5688 |
(helm-search-match-part cand pattern))) |
|
5689 |
do (progn |
|
5690 |
(puthash cand iter hash) |
|
5691 |
(helm--maybe-process-filter-one-by-one-candidate cand source) |
|
5692 |
(cl-incf count)) |
|
5693 |
and collect cand)))))) |
|
5694 |
|
|
5695 |
(defun helm-search-match-part (candidate pattern) |
|
5696 |
"Match PATTERN only on match-part property value of CANDIDATE. |
|
5697 |
|
|
5698 |
Because `helm-search-match-part' maybe called even if unspecified |
|
5699 |
in source (negation or fuzzy), the part to match fallback to the whole |
|
5700 |
candidate even if match-part haven't been computed by match-part-fn |
|
5701 |
and stored in the match-part property." |
|
5702 |
(let ((part (or (get-text-property 0 'match-part candidate) |
|
5703 |
candidate)) |
|
5704 |
(fuzzy-regexp (cadr (gethash 'helm-pattern helm--fuzzy-regexp-cache))) |
|
5705 |
(matchfn (if helm-migemo-mode |
|
5706 |
'helm-mm-migemo-string-match 'string-match))) |
|
5707 |
(if (string-match " " pattern) |
|
5708 |
(cl-loop for i in (helm-mm-split-pattern pattern) always |
|
5709 |
(if (string-match "\\`!" i) |
|
5710 |
(not (funcall matchfn (substring i 1) part)) |
|
5711 |
(funcall matchfn i part))) |
|
5712 |
(if (string-match "\\`!" pattern) |
|
5713 |
(if helm--in-fuzzy |
|
5714 |
;; Fuzzy regexp have already been |
|
5715 |
;; computed with substring 1. |
|
5716 |
(not (string-match fuzzy-regexp part)) |
|
5717 |
(not (funcall matchfn (substring pattern 1) part))) |
|
5718 |
(funcall matchfn (if helm--in-fuzzy fuzzy-regexp pattern) part))))) |
|
5719 |
|
|
5720 |
(defun helm-initial-candidates-from-candidate-buffer (get-line-fn limit) |
|
5721 |
(delq nil (cl-loop for i from 1 to limit |
|
5722 |
until (eobp) |
|
5723 |
collect (funcall get-line-fn |
|
5724 |
(point-at-bol) (point-at-eol)) |
|
5725 |
do (forward-line 1)))) |
|
5726 |
|
|
5727 |
(defun helm--search-from-candidate-buffer-1 (search-fn) |
|
5728 |
;; We are adding a newline at bob and at eol |
|
5729 |
;; and removing these newlines afterward. |
|
5730 |
;; This is a bad hack that should be removed. |
|
5731 |
;; To avoid matching the empty line at first line |
|
5732 |
;; when searching with e.g occur and "^$" just |
|
5733 |
;; forward-line before searching (See >>>[1] above). |
|
5734 |
(goto-char (point-min)) |
|
5735 |
(insert "\n") |
|
5736 |
(goto-char (point-max)) |
|
5737 |
(insert "\n") |
|
5738 |
(unwind-protect |
|
5739 |
(funcall search-fn) |
|
5740 |
(goto-char (point-min)) |
|
5741 |
(delete-char 1) |
|
5742 |
(goto-char (1- (point-max))) |
|
5743 |
(delete-char 1) |
|
5744 |
(set-buffer-modified-p nil))) |
|
5745 |
|
|
5746 |
(defun helm-candidate-buffer (&optional buffer-spec) |
|
5747 |
"Register and return a buffer storing candidates of current source. |
|
5748 |
|
|
5749 |
This is used to initialize a buffer for storing candidates for a |
|
5750 |
candidates-in-buffer source, candidates will be searched in this |
|
5751 |
buffer and displayed in `helm-buffer'. |
|
5752 |
This should be used only in init functions, don't relay on this in |
|
5753 |
other places unless you know what you are doing. |
|
5754 |
|
|
5755 |
This function is still in public API only for backward compatibility, |
|
5756 |
you should use instead `helm-init-candidates-in-buffer' for |
|
5757 |
initializing your sources. |
|
5758 |
|
|
5759 |
Internally, this function is called without argument and returns the |
|
5760 |
buffer corresponding to current source i.e `helm--source-name' which |
|
5761 |
is available in only some places. |
|
5762 |
|
|
5763 |
Acceptable values of BUFFER-SPEC: |
|
5764 |
|
|
5765 |
- global (a symbol) |
|
5766 |
Create a new global candidates buffer, |
|
5767 |
named \" *helm candidates:SOURCE*\". |
|
5768 |
This is used by `helm-init-candidates-in-buffer' and it is |
|
5769 |
the most common usage of BUFFER-SPEC. |
|
5770 |
The buffer will be killed and recreated at each new helm-session. |
|
5771 |
|
|
5772 |
- local (a symbol) |
|
5773 |
Create a new local candidates buffer, |
|
5774 |
named \" *helm candidates:SOURCE*HELM-CURRENT-BUFFER\". |
|
5775 |
You may want to use this when you want to have a different buffer |
|
5776 |
each time source is used from a different `helm-current-buffer'. |
|
5777 |
The buffer is erased and refilled at each new session but not killed. |
|
5778 |
You probably don't want to use this value for BUFFER-SPEC. |
|
5779 |
|
|
5780 |
- nil (omit) |
|
5781 |
Only return the candidates buffer of current source if found. |
|
5782 |
|
|
5783 |
- A buffer |
|
5784 |
Register a buffer as a candidates buffer. |
|
5785 |
The buffer needs to exists, it is not created. |
|
5786 |
This allow you to use the buffer as a cache, it is faster because |
|
5787 |
the buffer is already drawn, but be careful when using this as you |
|
5788 |
may mangle your buffer depending what you write in your init(s) |
|
5789 |
function, IOW don't modify the contents of the buffer in init(s) |
|
5790 |
function but in a transformer. |
|
5791 |
The buffer is not erased nor deleted. |
|
5792 |
Generally it is safer to use a copy of buffer inserted |
|
5793 |
in a global or local buffer. |
|
5794 |
|
|
5795 |
If for some reasons a global buffer and a local buffer exist and are |
|
5796 |
belonging to the same source, the local buffer takes precedence on the |
|
5797 |
global one and is used instead. |
|
5798 |
|
|
5799 |
When forcing update only the global and local buffers are killed |
|
5800 |
before running again the init function." |
|
5801 |
(let ((global-bname (format " *helm candidates:%s*" |
|
5802 |
helm--source-name)) |
|
5803 |
(local-bname (format " *helm candidates:%s*%s" |
|
5804 |
helm--source-name |
|
5805 |
(buffer-name helm-current-buffer)))) |
|
5806 |
(when buffer-spec |
|
5807 |
;; Register buffer in `helm--candidate-buffer-alist'. |
|
5808 |
;; This is used only to retrieve buffer associated to current source |
|
5809 |
;; when using named buffer as value of BUFFER-SPEC. |
|
5810 |
(setq helm--candidate-buffer-alist |
|
5811 |
(cons (cons helm--source-name buffer-spec) |
|
5812 |
(delete (assoc helm--source-name |
|
5813 |
helm--candidate-buffer-alist) |
|
5814 |
helm--candidate-buffer-alist))) |
|
5815 |
;; When using global or local as value of CREATE-OR-BUFFER |
|
5816 |
;; create the buffer global-bname or local-bname, otherwise |
|
5817 |
;; reuse the named buffer. |
|
5818 |
(unless (bufferp buffer-spec) |
|
5819 |
;; Global buffers are killed and recreated. |
|
5820 |
(and (eq buffer-spec 'global) |
|
5821 |
(buffer-live-p (get-buffer global-bname)) |
|
5822 |
(kill-buffer global-bname)) |
|
5823 |
;; Create global or local buffer. |
|
5824 |
;; Local buffer, once created are reused and a new one |
|
5825 |
;; is created when `helm-current-buffer' change across sessions. |
|
5826 |
(with-current-buffer (get-buffer-create |
|
5827 |
(cl-ecase buffer-spec |
|
5828 |
(global global-bname) |
|
5829 |
(local local-bname))) |
|
5830 |
;; We need a buffer not read-only to perhaps insert later |
|
5831 |
;; text coming from read-only buffers (issue #1176). |
|
5832 |
(set (make-local-variable 'buffer-read-only) nil) |
|
5833 |
;; Undo is automatically disabled in buffer names starting |
|
5834 |
;; with a space, so no need to disable it. |
|
5835 |
(erase-buffer) |
|
5836 |
(font-lock-mode -1)))) |
|
5837 |
;; Finally return the candidates buffer. |
|
5838 |
(helm-acond ((get-buffer local-bname)) |
|
5839 |
((get-buffer global-bname)) |
|
5840 |
((assoc-default helm--source-name helm--candidate-buffer-alist) |
|
5841 |
(and (or (stringp it) (bufferp it)) |
|
5842 |
(buffer-live-p (get-buffer it)) |
|
5843 |
it))))) |
|
5844 |
|
|
5845 |
(defun helm-init-candidates-in-buffer (buffer-spec data) |
|
5846 |
"Register BUFFER-SPEC with DATA for a helm candidates-in-buffer session. |
|
5847 |
|
|
5848 |
Arg BUFFER-SPEC can be a buffer-name (stringp), a buffer-spec object |
|
5849 |
\(bufferp), or a symbol, either 'local or 'global which is passed to |
|
5850 |
`helm-candidate-buffer'. |
|
5851 |
The most common usage of BUFFER-SPEC is 'global. |
|
5852 |
|
|
5853 |
Arg DATA can be either a list or a plain string. |
|
5854 |
Returns the resulting buffer. |
|
5855 |
|
|
5856 |
Use this in your init function to register a buffer for a |
|
5857 |
`helm-source-in-buffer' session and feed it with DATA. |
|
5858 |
You probably don't want to bother with this and use the :data slot |
|
5859 |
when initializing a source with `helm-source-in-buffer' class." |
|
5860 |
(declare (indent 1)) |
|
5861 |
(let ((caching (and (or (stringp buffer-spec) |
|
5862 |
(bufferp buffer-spec)) |
|
5863 |
(buffer-live-p (get-buffer buffer-spec)))) |
|
5864 |
(buf (helm-candidate-buffer |
|
5865 |
(if (or (stringp buffer-spec) |
|
5866 |
(bufferp buffer-spec)) |
|
5867 |
(get-buffer-create buffer-spec) |
|
5868 |
buffer-spec)))) ; a symbol 'global or 'local. |
|
5869 |
(unless caching |
|
5870 |
(with-current-buffer buf |
|
5871 |
(erase-buffer) |
|
5872 |
(cond ((listp data) |
|
5873 |
(insert (mapconcat (lambda (i) |
|
5874 |
(cond ((symbolp i) (symbol-name i)) |
|
5875 |
((numberp i) (number-to-string i)) |
|
5876 |
(t i))) |
|
5877 |
data "\n"))) |
|
5878 |
((stringp data) (insert data)))) |
|
5879 |
buf))) |
|
5880 |
|
|
5881 |
|
|
5882 |
;;; Resplit helm window |
|
5883 |
;; |
|
5884 |
;; |
|
5885 |
(defun helm-toggle-resplit-window () |
|
5886 |
"Toggle resplit helm window, vertically or horizontally." |
|
5887 |
(interactive) |
|
5888 |
(with-helm-alive-p |
|
5889 |
(if (= (length (window-list nil 1)) 2) |
|
5890 |
(progn |
|
5891 |
(when helm-prevent-escaping-from-minibuffer |
|
5892 |
(helm-prevent-switching-other-window :enabled nil)) |
|
5893 |
(unwind-protect |
|
5894 |
(with-helm-window |
|
5895 |
(cond ((or helm-full-frame (one-window-p t)) |
|
5896 |
(user-error "Attempt to resplit a single window")) |
|
5897 |
((helm-action-window) |
|
5898 |
(user-error "Can't resplit while selecting actions")) |
|
5899 |
(t |
|
5900 |
(let ((before-height (window-height))) |
|
5901 |
(delete-window) |
|
5902 |
(set-window-buffer |
|
5903 |
(select-window |
|
5904 |
(if (= (window-height) before-height) ; initial split was horizontal. |
|
5905 |
;; Split window vertically with `helm-buffer' placed |
|
5906 |
;; on the good side according to actual value of |
|
5907 |
;; `helm-split-window-default-side'. |
|
5908 |
(prog1 |
|
5909 |
(cond ((or (eq helm-split-window-default-side 'above) |
|
5910 |
(eq helm-split-window-default-side 'left)) |
|
5911 |
(split-window |
|
5912 |
(selected-window) nil 'above)) |
|
5913 |
(t (split-window-vertically))) |
|
5914 |
(setq helm-split-window-state 'vertical)) |
|
5915 |
;; Split window vertically, same comment as above. |
|
5916 |
(setq helm-split-window-state 'horizontal) |
|
5917 |
(cond ((or (eq helm-split-window-default-side 'left) |
|
5918 |
(eq helm-split-window-default-side 'above)) |
|
5919 |
(split-window (selected-window) nil 'left)) |
|
5920 |
(t (split-window-horizontally))))) |
|
5921 |
helm-buffer)))) |
|
5922 |
(setq helm--window-side-state (helm--get-window-side-state))) |
|
5923 |
(when helm-prevent-escaping-from-minibuffer |
|
5924 |
(helm-prevent-switching-other-window :enabled t)))) |
|
5925 |
(error "current window configuration not suitable for splitting")))) |
|
5926 |
(put 'helm-toggle-resplit-window 'helm-only t) |
|
5927 |
|
|
5928 |
;; Utility: Resize helm window. |
|
5929 |
(defun helm-enlarge-window-1 (n) |
|
5930 |
"Enlarge or narrow helm window. |
|
5931 |
If N is positive enlarge, if negative narrow." |
|
5932 |
(unless helm-full-frame |
|
5933 |
(let ((horizontal-p (eq helm-split-window-state 'horizontal))) |
|
5934 |
(with-helm-window |
|
5935 |
(enlarge-window n horizontal-p))))) |
|
5936 |
|
|
5937 |
(defun helm-narrow-window () |
|
5938 |
"Narrow helm window." |
|
5939 |
(interactive) |
|
5940 |
(with-helm-alive-p |
|
5941 |
(helm-enlarge-window-1 -1))) |
|
5942 |
(put 'helm-narrow-window 'helm-only t) |
|
5943 |
|
|
5944 |
(defun helm-enlarge-window () |
|
5945 |
"Enlarge helm window." |
|
5946 |
(interactive) |
|
5947 |
(with-helm-alive-p |
|
5948 |
(helm-enlarge-window-1 1))) |
|
5949 |
(put 'helm-enlarge-window 'helm-only t) |
|
5950 |
|
|
5951 |
(defun helm-toggle-full-frame (&optional arg) |
|
5952 |
"Toggle helm-buffer full-frame view." |
|
5953 |
(interactive "p") |
|
5954 |
(cl-assert (null (helm-action-window)) |
|
5955 |
nil "Unable to toggle full frame from action window") |
|
5956 |
(when arg ; Called interactively |
|
5957 |
(cl-assert (null helm--buffer-in-new-frame-p) |
|
5958 |
nil "Can't toggle full frame when using helm own frame")) |
|
5959 |
(if (or helm-onewindow-p |
|
5960 |
(buffer-local-value 'helm-full-frame (get-buffer helm-buffer))) |
|
5961 |
(with-helm-window |
|
5962 |
(setq-local helm-full-frame nil) |
|
5963 |
(setq helm-onewindow-p nil) |
|
5964 |
(let ((split-window-preferred-function |
|
5965 |
helm-split-window-preferred-function)) |
|
5966 |
(switch-to-buffer helm-current-buffer) |
|
5967 |
(helm-display-buffer helm-buffer) |
|
5968 |
(select-window (minibuffer-window)))) |
|
5969 |
(with-helm-window |
|
5970 |
(delete-other-windows) |
|
5971 |
(setq-local helm-full-frame t) |
|
5972 |
(setq helm-onewindow-p t)))) |
|
5973 |
(put 'helm-toggle-full-frame 'helm-only t) |
|
5974 |
|
|
5975 |
(defun helm-swap-windows () |
|
5976 |
"Swap window holding `helm-buffer' with other window." |
|
5977 |
(interactive) |
|
5978 |
(with-helm-alive-p |
|
5979 |
(if (= (length (window-list nil 1)) 2) |
|
5980 |
(cond ((and helm-full-frame (one-window-p t)) |
|
5981 |
(user-error "Can't swap windows in a single window")) |
|
5982 |
((helm-action-window) |
|
5983 |
(user-error "Can't resplit while selecting actions")) |
|
5984 |
(t |
|
5985 |
(let* ((w1 (helm-window)) |
|
5986 |
(split-state (eq helm-split-window-state 'horizontal)) |
|
5987 |
(w1size (window-total-size w1 split-state)) |
|
5988 |
(b1 (window-buffer w1)) ; helm-buffer |
|
5989 |
(s1 (window-start w1)) |
|
5990 |
(cur-frame (window-frame w1)) |
|
5991 |
(w2 (with-selected-window (helm-window) |
|
5992 |
;; Don't try to display helm-buffer |
|
5993 |
;; in a dedicated window. |
|
5994 |
(get-window-with-predicate |
|
5995 |
(lambda (w) (not (window-dedicated-p w))) |
|
5996 |
1 cur-frame))) |
|
5997 |
(w2size (window-total-size w2 split-state)) |
|
5998 |
(b2 (window-buffer w2)) ; probably helm-current-buffer |
|
5999 |
(s2 (window-start w2)) |
|
6000 |
resize) |
|
6001 |
(with-selected-frame (window-frame w1) |
|
6002 |
(helm-replace-buffer-in-window w1 b1 b2) |
|
6003 |
(helm-replace-buffer-in-window w2 b2 b1) |
|
6004 |
(setq resize |
|
6005 |
(cond ( ;; helm-window is smaller than other window. |
|
6006 |
(< w1size w2size) |
|
6007 |
(- (- (max w2size w1size) |
|
6008 |
(min w2size w1size)))) |
|
6009 |
( ;; helm-window is larger than other window. |
|
6010 |
(> w1size w2size) |
|
6011 |
(- (max w2size w1size) |
|
6012 |
(min w2size w1size))) |
|
6013 |
( ;; windows have probably same size. |
|
6014 |
t nil))) |
|
6015 |
;; Maybe resize the window holding helm-buffer. |
|
6016 |
(and resize (window-resize w2 resize split-state)) |
|
6017 |
(set-window-start w1 s2 t) |
|
6018 |
(set-window-start w2 s1 t)) |
|
6019 |
(setq helm--window-side-state (helm--get-window-side-state))))) |
|
6020 |
(error "current window configuration not suitable for splitting")))) |
|
6021 |
(put 'helm-swap-windows 'helm-only t) |
|
6022 |
|
|
6023 |
(defun helm--get-window-side-state () |
|
6024 |
"Return the position of `helm-window' from `helm-current-buffer'. |
|
6025 |
Possible values are 'left 'right 'below or 'above." |
|
6026 |
(let ((side-list '(left right below above))) |
|
6027 |
(cl-loop for side in side-list |
|
6028 |
thereis (and (equal (helm-window) |
|
6029 |
(window-in-direction |
|
6030 |
side (get-buffer-window helm-current-buffer t) |
|
6031 |
t)) |
|
6032 |
side)))) |
|
6033 |
|
|
6034 |
(defun helm-replace-buffer-in-window (window buffer1 buffer2) |
|
6035 |
"Replace BUFFER1 by BUFFER2 in WINDOW registering BUFFER1." |
|
6036 |
(when (get-buffer-window buffer1) |
|
6037 |
(unrecord-window-buffer window buffer1) |
|
6038 |
(set-window-buffer window buffer2))) |
|
6039 |
|
|
6040 |
;; Utility: select another action by key |
|
6041 |
(defun helm-select-nth-action (n) |
|
6042 |
"Select the N nth action for the currently selected candidate." |
|
6043 |
(let ((src (helm-get-current-source))) |
|
6044 |
(setq helm-saved-selection (helm-get-selection nil nil src)) |
|
6045 |
(unless helm-saved-selection |
|
6046 |
(error "Nothing is selected")) |
|
6047 |
(setq helm-saved-action |
|
6048 |
(helm-get-nth-action |
|
6049 |
n |
|
6050 |
(if (get-buffer-window helm-action-buffer 'visible) |
|
6051 |
(assoc-default 'candidates src) |
|
6052 |
(helm-get-actions-from-current-source src)))) |
|
6053 |
(helm-maybe-exit-minibuffer))) |
|
6054 |
|
|
6055 |
(defun helm-get-nth-action (n action) |
|
6056 |
(cond ((and (zerop n) (functionp action)) |
|
6057 |
action) |
|
6058 |
((listp action) |
|
6059 |
(or (cdr (elt action n)) |
|
6060 |
(error "No such action"))) |
|
6061 |
((and (functionp action) (< 0 n)) |
|
6062 |
(error "Sole action")) |
|
6063 |
(t |
|
6064 |
(error "Error in `helm-select-nth-action'")))) |
|
6065 |
|
|
6066 |
(defun helm-execute-selection-action-at-nth (linum) |
|
6067 |
"Execute default action on candidate at LINUM lines from selection." |
|
6068 |
(let ((prefarg current-prefix-arg)) |
|
6069 |
(if (>= linum 0) |
|
6070 |
(helm-next-line linum) |
|
6071 |
(helm-previous-line (lognot (1- linum)))) |
|
6072 |
(setq current-prefix-arg prefarg) |
|
6073 |
(helm-exit-minibuffer))) |
|
6074 |
|
|
6075 |
;;; Persistent Action |
|
6076 |
;; |
|
6077 |
(defun helm-initialize-persistent-action () |
|
6078 |
(set (make-local-variable 'helm-persistent-action-display-window) nil)) |
|
6079 |
|
|
6080 |
(cl-defun helm-execute-persistent-action (&optional attr split-onewindow) |
|
6081 |
"Perform the associated action ATTR without quitting helm. |
|
6082 |
Arg ATTR default will be `persistent-action' or `persistent-action-if' |
|
6083 |
if unspecified depending on what's found in source, but it can be |
|
6084 |
anything else. |
|
6085 |
In this case you have to add this new attribute to your source. |
|
6086 |
See `persistent-action' and `persistent-action-if' slot documentation |
|
6087 |
in `helm-source'. |
|
6088 |
|
|
6089 |
When `helm-full-frame' or SPLIT-ONEWINDOW are non-`nil', and |
|
6090 |
`helm-buffer' is displayed in only one window, the helm window is |
|
6091 |
split to display `helm-select-persistent-action-window' in other |
|
6092 |
window to maintain visibility." |
|
6093 |
(interactive) |
|
6094 |
(with-helm-alive-p |
|
6095 |
(let ((source (helm-get-current-source))) |
|
6096 |
(unless attr |
|
6097 |
(setq attr (or (car (assq 'persistent-action source)) |
|
6098 |
(car (assq 'persistent-action-if source))))) |
|
6099 |
(helm-log "executing persistent-action") |
|
6100 |
(let* ((selection (and source (helm-get-selection nil nil source))) |
|
6101 |
(attr-val (if (eq attr 'persistent-action-if) |
|
6102 |
(funcall (assoc-default attr source) selection) |
|
6103 |
(assoc-default attr source))) |
|
6104 |
;; If attr value is a cons, use its car as persistent function |
|
6105 |
;; and its car to decide if helm window should be splitted. |
|
6106 |
(fn (if (and (consp attr-val) |
|
6107 |
;; maybe a lambda. |
|
6108 |
(not (functionp attr-val))) |
|
6109 |
(car attr-val) attr-val)) |
|
6110 |
(no-split (and (consp attr-val) |
|
6111 |
(not (functionp attr-val)) |
|
6112 |
(cdr attr-val))) |
|
6113 |
(cursor-in-echo-area t) |
|
6114 |
mode-line-in-non-selected-windows) |
|
6115 |
(progn |
|
6116 |
(when (and helm-onewindow-p (null no-split) |
|
6117 |
(null helm--buffer-in-new-frame-p)) |
|
6118 |
(helm-toggle-full-frame)) |
|
6119 |
(when (eq fn 'ignore) |
|
6120 |
(cl-return-from helm-execute-persistent-action nil)) |
|
6121 |
(when source |
|
6122 |
(with-helm-window |
|
6123 |
(save-selected-window |
|
6124 |
(if no-split |
|
6125 |
(helm-select-persistent-action-window) |
|
6126 |
(helm-select-persistent-action-window |
|
6127 |
(or split-onewindow helm-onewindow-p))) |
|
6128 |
(helm-log "current-buffer = %S" (current-buffer)) |
|
6129 |
(let ((helm-in-persistent-action t) |
|
6130 |
(same-window-regexps '(".")) |
|
6131 |
display-buffer-function pop-up-windows pop-up-frames |
|
6132 |
special-display-regexps special-display-buffer-names) |
|
6133 |
(helm-execute-selection-action-1 |
|
6134 |
selection (or fn (helm-get-actions-from-current-source source)) t) |
|
6135 |
(unless (helm-action-window) |
|
6136 |
(helm-log-run-hook 'helm-after-persistent-action-hook))) |
|
6137 |
;; A typical case is when a persistent action delete |
|
6138 |
;; the buffer already displayed in |
|
6139 |
;; `helm-persistent-action-display-window' and `helm-full-frame' |
|
6140 |
;; is enabled, we end up with the `helm-buffer' |
|
6141 |
;; displayed in two windows. |
|
6142 |
(when (and helm-onewindow-p |
|
6143 |
(> (length (window-list)) 1) |
|
6144 |
(equal (buffer-name |
|
6145 |
(window-buffer |
|
6146 |
helm-persistent-action-display-window)) |
|
6147 |
(helm-buffer-get))) |
|
6148 |
(delete-other-windows)))))))))) |
|
6149 |
(put 'helm-execute-persistent-action 'helm-only t) |
|
6150 |
|
|
6151 |
(defun helm-persistent-action-display-window (&optional split-onewindow) |
|
6152 |
"Return the window that will be used for persistent action. |
|
6153 |
If SPLIT-ONEWINDOW is non-`nil' window is split in persistent action." |
|
6154 |
(with-helm-window |
|
6155 |
(let (next-win cur-win) |
|
6156 |
(setq helm-persistent-action-display-window |
|
6157 |
(cond ((and (window-live-p helm-persistent-action-display-window) |
|
6158 |
(not (member helm-persistent-action-display-window |
|
6159 |
(get-buffer-window-list helm-buffer)))) |
|
6160 |
helm-persistent-action-display-window) |
|
6161 |
((and helm--buffer-in-new-frame-p helm-initial-frame) |
|
6162 |
(with-selected-frame helm-initial-frame (selected-window))) |
|
6163 |
(split-onewindow (split-window)) |
|
6164 |
;; Fix Issue #2050 with dedicated window. |
|
6165 |
((window-dedicated-p |
|
6166 |
(setq next-win (next-window (selected-window) 1))) |
|
6167 |
(with-helm-after-update-hook |
|
6168 |
(and (window-live-p helm-persistent-action-display-window) |
|
6169 |
(delete-window helm-persistent-action-display-window))) |
|
6170 |
(split-window)) |
|
6171 |
((window-dedicated-p |
|
6172 |
(setq cur-win (get-buffer-window helm-current-buffer))) |
|
6173 |
(split-window)) |
|
6174 |
(cur-win) |
|
6175 |
(t next-win)))))) |
|
6176 |
|
|
6177 |
(defun helm-select-persistent-action-window (&optional split-onewindow) |
|
6178 |
"Select the window that will be used for persistent action. |
|
6179 |
See `helm-persistent-action-display-window' for how to use SPLIT-ONEWINDOW." |
|
6180 |
(select-window (get-buffer-window (helm-buffer-get))) |
|
6181 |
(prog1 |
|
6182 |
(select-window |
|
6183 |
(setq minibuffer-scroll-window |
|
6184 |
(helm-persistent-action-display-window split-onewindow))) |
|
6185 |
(helm-log "Selected window is %S" minibuffer-scroll-window))) |
|
6186 |
|
|
6187 |
;;; Scrolling - recentering |
|
6188 |
;; |
|
6189 |
;; |
|
6190 |
(defun helm-other-window-base (command &optional arg) |
|
6191 |
(let ((minibuffer-scroll-window |
|
6192 |
(helm-persistent-action-display-window))) |
|
6193 |
(funcall command (or arg helm-scroll-amount)))) |
|
6194 |
|
|
6195 |
(defun helm-scroll-other-window (&optional arg) |
|
6196 |
"Scroll other window upward ARG many lines. |
|
6197 |
When arg is not provided scroll `helm-scroll-amount' lines. |
|
6198 |
See `scroll-other-window'." |
|
6199 |
(interactive "P") |
|
6200 |
(with-helm-alive-p (helm-other-window-base 'scroll-other-window arg))) |
|
6201 |
(put 'helm-scroll-other-window 'helm-only t) |
|
6202 |
|
|
6203 |
(defun helm-scroll-other-window-down (&optional arg) |
|
6204 |
"Scroll other window downward ARG many lines. |
|
6205 |
When arg is not provided scroll `helm-scroll-amount' lines. |
|
6206 |
See `scroll-other-window-down'." |
|
6207 |
(interactive "P") |
|
6208 |
(with-helm-alive-p (helm-other-window-base 'scroll-other-window-down arg))) |
|
6209 |
(put 'helm-scroll-other-window-down 'helm-only t) |
|
6210 |
|
|
6211 |
(defun helm-recenter-top-bottom-other-window (&optional arg) |
|
6212 |
"Run `recenter-top-bottom' in other window. |
|
6213 |
Meaning of prefix ARG is the same as in `recenter-top-bottom'." |
|
6214 |
(interactive "P") |
|
6215 |
(with-helm-alive-p |
|
6216 |
(with-helm-window |
|
6217 |
(with-selected-window (helm-persistent-action-display-window) |
|
6218 |
(recenter-top-bottom arg))))) |
|
6219 |
(put 'helm-recenter-top-bottom-other-window 'helm-only t) |
|
6220 |
|
|
6221 |
(defun helm-reposition-window-other-window (&optional arg) |
|
6222 |
"Run `reposition-window' in other window. |
|
6223 |
Meaning of prefix ARG is the same as in `reposition-window'." |
|
6224 |
(interactive "P") |
|
6225 |
(with-helm-alive-p |
|
6226 |
(with-helm-window |
|
6227 |
(with-selected-window (helm-persistent-action-display-window) |
|
6228 |
(reposition-window arg))))) |
|
6229 |
(put 'helm-reposition-window-other-window 'helm-only t) |
|
6230 |
|
|
6231 |
|
|
6232 |
;; Utility: Visible Mark |
|
6233 |
|
|
6234 |
(defun helm-clear-visible-mark () |
|
6235 |
(with-current-buffer (helm-buffer-get) |
|
6236 |
(mapc 'delete-overlay helm-visible-mark-overlays) |
|
6237 |
(set (make-local-variable 'helm-visible-mark-overlays) nil))) |
|
6238 |
|
|
6239 |
(defun helm-this-visible-mark () |
|
6240 |
(cl-loop for o in (overlays-at (point)) |
|
6241 |
when (overlay-get o 'visible-mark) |
|
6242 |
return o)) |
|
6243 |
|
|
6244 |
(defun helm-delete-visible-mark (overlay) |
|
6245 |
(let ((src (helm-get-current-source))) |
|
6246 |
(setq helm-marked-candidates |
|
6247 |
(remove |
|
6248 |
(cons src (helm-get-selection nil nil src)) |
|
6249 |
helm-marked-candidates)) |
|
6250 |
(delete-overlay overlay) |
|
6251 |
(setq helm-visible-mark-overlays |
|
6252 |
(delq overlay helm-visible-mark-overlays)))) |
|
6253 |
|
|
6254 |
(defun helm-make-visible-mark (&optional src selection) |
|
6255 |
(let* ((source (or src (helm-get-current-source))) |
|
6256 |
(sel (or selection (helm-get-selection |
|
6257 |
nil (helm-attr 'marked-with-props source) |
|
6258 |
source))) |
|
6259 |
(selection-end (if (helm-pos-multiline-p) |
|
6260 |
;; Stays within source |
|
6261 |
(or (helm-get-next-candidate-separator-pos) |
|
6262 |
(helm-get-next-header-pos) |
|
6263 |
(point-max)) |
|
6264 |
;; Not multiline |
|
6265 |
(1+ (point-at-eol)))) |
|
6266 |
(o (make-overlay (point-at-bol) selection-end))) |
|
6267 |
(overlay-put o 'priority 0) |
|
6268 |
(overlay-put o 'face 'helm-visible-mark) |
|
6269 |
(overlay-put o 'source (assoc-default 'name source)) |
|
6270 |
(overlay-put o 'string (buffer-substring (overlay-start o) (overlay-end o))) |
|
6271 |
(overlay-put o 'real sel) |
|
6272 |
(overlay-put o 'visible-mark t) |
|
6273 |
(cl-pushnew o helm-visible-mark-overlays) |
|
6274 |
(push (cons source sel) helm-marked-candidates))) |
|
6275 |
|
|
6276 |
(defun helm-toggle-visible-mark (arg) |
|
6277 |
"Toggle helm visible mark at point ARG times. |
|
6278 |
If ARG is negative toggle backward." |
|
6279 |
(interactive "p") |
|
6280 |
(with-helm-alive-p |
|
6281 |
(with-helm-window |
|
6282 |
(let ((nomark (assq 'nomark (helm-get-current-source))) |
|
6283 |
(next-fns (if (< arg 0) |
|
6284 |
'(helm-beginning-of-source-p . helm-previous-line) |
|
6285 |
'(helm-end-of-source-p . helm-next-line)))) |
|
6286 |
(if nomark |
|
6287 |
(message "Marking not allowed in this source") |
|
6288 |
(cl-loop with n = (if (< arg 0) (* arg -1) arg) |
|
6289 |
repeat n do |
|
6290 |
(progn |
|
6291 |
(helm-aif (helm-this-visible-mark) |
|
6292 |
(helm-delete-visible-mark it) |
|
6293 |
(helm-make-visible-mark)) |
|
6294 |
(if (funcall (car next-fns)) |
|
6295 |
(progn |
|
6296 |
(helm-display-mode-line (helm-get-current-source)) |
|
6297 |
(cl-return nil)) |
|
6298 |
(funcall (cdr next-fns)))))))))) |
|
6299 |
(put 'helm-toggle-visible-mark 'helm-only t) |
|
6300 |
|
|
6301 |
(defun helm-file-completion-source-p (&optional source) |
|
6302 |
"Return non-`nil' if current source is a file completion source." |
|
6303 |
(or helm--completing-file-name ; helm-read-file-name |
|
6304 |
(let ((cur-source (cdr (assq 'name |
|
6305 |
(or source (helm-get-current-source)))))) |
|
6306 |
(cl-loop for i in helm--file-completion-sources |
|
6307 |
thereis (string= cur-source i))))) |
|
6308 |
|
|
6309 |
(defun helm-mark-all (&optional all) |
|
6310 |
"Mark all visible unmarked candidates in current source. |
|
6311 |
|
|
6312 |
With a prefix arg mark all visible unmarked candidates in all sources." |
|
6313 |
(interactive "P") |
|
6314 |
(with-helm-alive-p |
|
6315 |
(with-helm-window ; Using `with-helm-buffer' for some unknow reasons infloop. |
|
6316 |
(if (null all) |
|
6317 |
(helm-mark-all-1 t) |
|
6318 |
(let ((pos (point))) |
|
6319 |
(goto-char (point-min)) |
|
6320 |
(helm-awhile (helm-get-next-header-pos) |
|
6321 |
(goto-char it) |
|
6322 |
(forward-line 1) |
|
6323 |
(helm-mark-current-line) |
|
6324 |
(helm-mark-all-1)) |
|
6325 |
;; `save-excursion' seems confused if used in addition of |
|
6326 |
;; the one used in `helm-mark-all-1', so save POS and back |
|
6327 |
;; to it when loop is finished. |
|
6328 |
(goto-char pos) |
|
6329 |
(helm-mark-current-line) |
|
6330 |
(helm-display-mode-line (helm-get-current-source) t)))))) |
|
6331 |
(put 'helm-mark-all 'helm-only t) |
|
6332 |
|
|
6333 |
(defun helm-mark-all-1 (&optional ensure-beg-of-source) |
|
6334 |
"Mark all visible unmarked candidates in current source. |
|
6335 |
Need to be wrapped in `with-helm-window'. |
|
6336 |
Arg ENSURE-BEG-OF-SOURCE ensure we are at beginning of source when |
|
6337 |
starting to mark candidates, if handled elsewhere before starting it |
|
6338 |
is not needed." |
|
6339 |
(let* ((src (helm-get-current-source)) |
|
6340 |
(follow (if (helm-follow-mode-p src) 1 -1)) |
|
6341 |
(nomark (assq 'nomark src)) |
|
6342 |
(src-name (assoc-default 'name src)) |
|
6343 |
(filecomp-p (or (helm-file-completion-source-p src) |
|
6344 |
(string= src-name "Files from Current Directory"))) |
|
6345 |
(remote-p (and filecomp-p (file-remote-p helm-pattern)))) |
|
6346 |
;; Note that `cl-letf' prevents edebug working properly. |
|
6347 |
(cl-letf (((symbol-function 'message) #'ignore)) |
|
6348 |
(helm-follow-mode -1) |
|
6349 |
(unwind-protect |
|
6350 |
(if nomark |
|
6351 |
(message "Marking not allowed in this source") |
|
6352 |
(save-excursion |
|
6353 |
(when ensure-beg-of-source |
|
6354 |
(goto-char (helm-get-previous-header-pos)) |
|
6355 |
(forward-line 1)) |
|
6356 |
(let* ((next-head (helm-get-next-header-pos)) |
|
6357 |
(end (and next-head |
|
6358 |
(save-excursion |
|
6359 |
(goto-char next-head) |
|
6360 |
(forward-line -1) |
|
6361 |
(point)))) |
|
6362 |
(maxpoint (or end (point-max)))) |
|
6363 |
(while (< (point) maxpoint) |
|
6364 |
(helm-mark-current-line) |
|
6365 |
(let* ((prefix (get-text-property (point-at-bol) 'display)) |
|
6366 |
(cand (helm-get-selection nil nil src)) |
|
6367 |
(bn (and filecomp-p (helm-basename cand)))) |
|
6368 |
;; Don't mark possibles directories ending with . or .. |
|
6369 |
;; autosave files/links and non--existent files. |
|
6370 |
(unless |
|
6371 |
(or (helm-this-visible-mark) |
|
6372 |
(string= prefix "[?]") ; doesn't match |
|
6373 |
(and filecomp-p |
|
6374 |
(or |
|
6375 |
;; autosave files |
|
6376 |
(string-match-p "^[.]?#.*#?$" bn) |
|
6377 |
;; dot files |
|
6378 |
(member bn '("." "..")) |
|
6379 |
;; We need to test here when not using |
|
6380 |
;; a transformer that put a prefix tag |
|
6381 |
;; before candidate. |
|
6382 |
;; (i.e no [?] prefix on tramp). |
|
6383 |
(and remote-p (not (file-exists-p cand)))))) |
|
6384 |
(helm-make-visible-mark src cand))) |
|
6385 |
(when (helm-pos-multiline-p) |
|
6386 |
(goto-char |
|
6387 |
(or (helm-get-next-candidate-separator-pos) |
|
6388 |
(point-max)))) |
|
6389 |
(forward-line 1)))) |
|
6390 |
(helm-mark-current-line)) |
|
6391 |
(helm-follow-mode follow))))) |
|
6392 |
|
|
6393 |
(defun helm-unmark-all () |
|
6394 |
"Unmark all candidates in all sources of current helm session." |
|
6395 |
(interactive) |
|
6396 |
(with-helm-alive-p |
|
6397 |
(with-helm-window |
|
6398 |
(save-excursion |
|
6399 |
(helm-clear-visible-mark)) |
|
6400 |
(setq helm-marked-candidates nil) |
|
6401 |
(helm-mark-current-line) |
|
6402 |
(helm-display-mode-line (helm-get-current-source))))) |
|
6403 |
(put 'helm-unmark-all 'helm-only t) |
|
6404 |
|
|
6405 |
(defun helm-toggle-all-marks (&optional all) |
|
6406 |
"Toggle all marks. |
|
6407 |
|
|
6408 |
Mark all visible candidates of current source or unmark all candidates |
|
6409 |
visible or invisible in all sources of current helm session. |
|
6410 |
|
|
6411 |
With a prefix argument mark all candidates in all sources." |
|
6412 |
(interactive "P") |
|
6413 |
(with-helm-alive-p |
|
6414 |
(let ((marked (helm-marked-candidates))) |
|
6415 |
(if (and (>= (length marked) 1) |
|
6416 |
(with-helm-window helm-visible-mark-overlays)) |
|
6417 |
(helm-unmark-all) |
|
6418 |
(helm-mark-all all))))) |
|
6419 |
(put 'helm-toggle-all-marks 'helm-only t) |
|
6420 |
|
|
6421 |
(defun helm--compute-marked (real source &optional wildcard) |
|
6422 |
(let* ((coerced (helm-coerce-selection real source)) |
|
6423 |
(wilds (and wildcard |
|
6424 |
(condition-case nil |
|
6425 |
(helm-file-expand-wildcards |
|
6426 |
coerced t) |
|
6427 |
(error nil))))) |
|
6428 |
;; Avoid returning a not expanded wilcard fname. |
|
6429 |
;; e.g assuming "/tmp" doesn't contain "*.el" |
|
6430 |
;; return nil when coerced is "/tmp/*.el". |
|
6431 |
(unless (or wilds (null wildcard) |
|
6432 |
(string-match-p helm--url-regexp coerced) |
|
6433 |
(file-exists-p coerced) |
|
6434 |
(and (stringp coerced) |
|
6435 |
(null (string-match-p "[[*?]" coerced)))) |
|
6436 |
(setq coerced nil)) |
|
6437 |
(or wilds (and coerced (list coerced))))) |
|
6438 |
|
|
6439 |
(cl-defun helm-marked-candidates (&key with-wildcard all-sources) |
|
6440 |
"Return marked candidates of current source, if any. |
|
6441 |
|
|
6442 |
Otherwise return one element list consisting of the current |
|
6443 |
selection. When key WITH-WILDCARD is specified, expand it. |
|
6444 |
When ALL-SOURCES key value is non-nil returns marked candidates of all |
|
6445 |
sources." |
|
6446 |
(with-current-buffer helm-buffer |
|
6447 |
(let* ((current-src (helm-get-current-source)) |
|
6448 |
(candidates |
|
6449 |
(cl-loop for (source . real) in (reverse helm-marked-candidates) |
|
6450 |
for use-wc = (and with-wildcard |
|
6451 |
(string-match-p "\\*" real) |
|
6452 |
(null (file-exists-p real))) |
|
6453 |
when (or all-sources |
|
6454 |
(equal (assq 'name source) |
|
6455 |
(assq 'name current-src))) |
|
6456 |
append (helm--compute-marked real source use-wc))) |
|
6457 |
sel) |
|
6458 |
(unless candidates |
|
6459 |
(setq sel (helm-get-selection |
|
6460 |
nil (helm-attr 'marked-with-props |
|
6461 |
current-src) |
|
6462 |
current-src)) |
|
6463 |
(setq candidates |
|
6464 |
(helm--compute-marked |
|
6465 |
sel current-src |
|
6466 |
(and with-wildcard (null (file-exists-p sel)))))) |
|
6467 |
(helm-log "Marked candidates = %S" candidates) |
|
6468 |
candidates))) |
|
6469 |
|
|
6470 |
(defun helm--remove-marked-and-update-mode-line (elm) |
|
6471 |
(with-helm-buffer |
|
6472 |
(setq helm-marked-candidates |
|
6473 |
(delete (rassoc elm helm-marked-candidates) |
|
6474 |
helm-marked-candidates)) |
|
6475 |
(helm-display-mode-line (helm-get-current-source)))) |
|
6476 |
|
|
6477 |
(defun helm-current-source-name= (name) |
|
6478 |
(save-excursion |
|
6479 |
(goto-char (helm-get-previous-header-pos)) |
|
6480 |
(equal name (helm-current-line-contents)))) |
|
6481 |
|
|
6482 |
(defun helm-revive-visible-mark () |
|
6483 |
"Restore marked candidates when helm updates display." |
|
6484 |
(with-current-buffer helm-buffer |
|
6485 |
(save-excursion |
|
6486 |
(cl-dolist (o helm-visible-mark-overlays) |
|
6487 |
(let ((o-src-str (overlay-get o 'source)) |
|
6488 |
(o-str (overlay-get o 'string)) |
|
6489 |
beg end) |
|
6490 |
;; Move point to end of source header line. |
|
6491 |
(goto-char (point-min)) |
|
6492 |
(search-forward o-src-str nil t) |
|
6493 |
(while (and (search-forward o-str nil t) |
|
6494 |
(cl-loop for ov in (overlays-at (point-at-bol 0)) |
|
6495 |
never (overlay-get ov 'visible-mark)) |
|
6496 |
(helm-current-source-name= o-src-str)) |
|
6497 |
(setq beg (match-beginning 0) |
|
6498 |
end (match-end 0)) |
|
6499 |
;; Calculate real value of candidate. |
|
6500 |
;; It can be nil if candidate have only a display value. |
|
6501 |
(let ((real (get-text-property (point-at-bol 0) 'helm-realvalue))) |
|
6502 |
(if real |
|
6503 |
;; Check if real value of current candidate is the same |
|
6504 |
;; than the one stored in overlay. |
|
6505 |
;; This is needed when some cands have same display names. |
|
6506 |
;; Using equal allow testing any type of value for real cand. |
|
6507 |
;; Issue (#706). |
|
6508 |
(and (equal (overlay-get o 'real) real) |
|
6509 |
(move-overlay o beg end)) |
|
6510 |
(and (equal o-str (buffer-substring beg end)) |
|
6511 |
(move-overlay o beg end)))))))))) |
|
6512 |
(add-hook 'helm-after-update-hook 'helm-revive-visible-mark) |
|
6513 |
|
|
6514 |
(defun helm-next-point-in-list (curpos points &optional prev) |
|
6515 |
(cond |
|
6516 |
;; rule out special cases. |
|
6517 |
((null points) curpos) |
|
6518 |
((and prev (<= curpos (car points))) |
|
6519 |
(nth (1- (length points)) points)) |
|
6520 |
((< (car (last points)) curpos) |
|
6521 |
(if prev (car (last points)) (nth 0 points))) |
|
6522 |
((and (not prev) (>= curpos (car (last points)))) |
|
6523 |
(nth 0 points)) |
|
6524 |
(t |
|
6525 |
(nth (if prev |
|
6526 |
(cl-loop for pt in points |
|
6527 |
for i from 0 |
|
6528 |
if (<= curpos pt) return (1- i)) |
|
6529 |
(cl-loop for pt in points |
|
6530 |
for i from 0 |
|
6531 |
if (< curpos pt) return i)) |
|
6532 |
points)))) |
|
6533 |
|
|
6534 |
(defun helm-next-visible-mark (&optional prev) |
|
6535 |
"Move next helm visible mark. |
|
6536 |
If PREV is non-`nil' move to precedent." |
|
6537 |
(interactive) |
|
6538 |
(with-helm-alive-p |
|
6539 |
(with-helm-window |
|
6540 |
(ignore-errors |
|
6541 |
(goto-char (helm-next-point-in-list |
|
6542 |
(point) |
|
6543 |
(sort (mapcar 'overlay-start helm-visible-mark-overlays) '<) |
|
6544 |
prev))) |
|
6545 |
(helm-mark-current-line)))) |
|
6546 |
(put 'helm-next-visible-mark 'helm-only t) |
|
6547 |
|
|
6548 |
(defun helm-prev-visible-mark () |
|
6549 |
"Move previous helm visible mark." |
|
6550 |
(interactive) |
|
6551 |
(with-helm-alive-p |
|
6552 |
(helm-next-visible-mark t))) |
|
6553 |
(put 'helm-prev-visible-mark 'helm-only t) |
|
6554 |
|
|
6555 |
;;; Utility: Selection Paste |
|
6556 |
;; |
|
6557 |
(defun helm-yank-selection (arg) |
|
6558 |
"Set minibuffer contents to current display selection. |
|
6559 |
With a prefix arg set to real value of current selection." |
|
6560 |
(interactive "P") |
|
6561 |
(with-helm-alive-p |
|
6562 |
(let ((str (format "%s" (helm-get-selection nil (not arg))))) |
|
6563 |
(kill-new str) |
|
6564 |
(helm-set-pattern str)))) |
|
6565 |
(put 'helm-yank-selection 'helm-only t) |
|
6566 |
|
|
6567 |
(defun helm-kill-selection-and-quit (arg) |
|
6568 |
"Store display value of current selection to kill ring. |
|
6569 |
With a prefix arg use real value of current selection. |
|
6570 |
Display value is shown in `helm-buffer' and real value |
|
6571 |
is used to perform actions." |
|
6572 |
(interactive "P") |
|
6573 |
(with-helm-alive-p |
|
6574 |
(helm-run-after-exit |
|
6575 |
(lambda (sel) |
|
6576 |
(kill-new sel) |
|
6577 |
;; Return nil to force `helm-mode--keyboard-quit' |
|
6578 |
;; in `helm-comp-read' otherwise the value "Saved to kill-ring: foo" |
|
6579 |
;; is used as exit value for `helm-comp-read'. |
|
6580 |
(prog1 nil (message "Saved to kill-ring: %s" sel) (sit-for 1))) |
|
6581 |
(format "%s" (helm-get-selection nil (not arg)))))) |
|
6582 |
(put 'helm-kill-selection-and-quit 'helm-only t) |
|
6583 |
|
|
6584 |
(defun helm-copy-to-buffer () |
|
6585 |
"Copy selection or marked candidates to `helm-current-buffer'. |
|
6586 |
Note that the real values of candidates are copied and not the |
|
6587 |
display values." |
|
6588 |
(interactive) |
|
6589 |
(with-helm-alive-p |
|
6590 |
(helm-run-after-exit |
|
6591 |
(lambda (cands) |
|
6592 |
(with-helm-current-buffer |
|
6593 |
(insert (mapconcat (lambda (c) |
|
6594 |
(format "%s" c)) |
|
6595 |
cands "\n")))) |
|
6596 |
(helm-marked-candidates)))) |
|
6597 |
(put 'helm-copy-to-buffer 'helm-only t) |
|
6598 |
|
|
6599 |
|
|
6600 |
;;; Follow-mode: Automatic execution of persistent-action |
|
6601 |
;; |
|
6602 |
;; |
|
6603 |
(defvar helm-follow-input-idle-delay nil |
|
6604 |
"`helm-follow-mode' will execute its persistent action after this delay. |
|
6605 |
Note that if the `follow-delay' attr is present in source, |
|
6606 |
it will take precedence over this.") |
|
6607 |
|
|
6608 |
(defun helm-follow-mode (&optional arg) |
|
6609 |
"Execute persistent action every time the cursor is moved. |
|
6610 |
|
|
6611 |
This mode is source local, i.e It apply on current source only. |
|
6612 |
\\<helm-map> |
|
6613 |
This mode can be enabled or disabled interactively at anytime during |
|
6614 |
a helm session with \\[helm-follow-mode]. |
|
6615 |
|
|
6616 |
When enabling interactively `helm-follow-mode' in a source, you can keep it enabled |
|
6617 |
for next emacs sessions by setting `helm-follow-mode-persistent' to a non-nil value. |
|
6618 |
|
|
6619 |
When `helm-follow-mode' is called with a prefix arg and `helm-follow-mode-persistent' |
|
6620 |
is non-nil `helm-follow-mode' will be persistent only for this emacs session, |
|
6621 |
but not for next emacs sessions, i.e the current source will not be saved |
|
6622 |
to `helm-source-names-using-follow'. |
|
6623 |
A prefix arg with `helm-follow-mode' already enabled will have no effect. |
|
6624 |
|
|
6625 |
Note that you can use instead of this mode the commands `helm-follow-action-forward' |
|
6626 |
and `helm-follow-action-backward' at anytime in all helm sessions. |
|
6627 |
|
|
6628 |
They are bound by default to \\[helm-follow-action-forward] and \\[helm-follow-action-backward]." |
|
6629 |
(interactive (list (helm-aif (and current-prefix-arg |
|
6630 |
(prefix-numeric-value current-prefix-arg)) |
|
6631 |
(unless (helm-follow-mode-p) it)))) |
|
6632 |
(with-helm-alive-p |
|
6633 |
(with-current-buffer helm-buffer |
|
6634 |
(let* ((src (helm-get-current-source)) |
|
6635 |
(name (assoc-default 'name src)) |
|
6636 |
(fol-attr (assq 'follow src)) |
|
6637 |
(enabled (or (helm-follow-mode-p src) |
|
6638 |
(and helm-follow-mode-persistent |
|
6639 |
(member (assoc-default 'name src) |
|
6640 |
helm-source-names-using-follow))))) |
|
6641 |
(if src |
|
6642 |
(progn |
|
6643 |
(if (eq (cdr fol-attr) 'never) |
|
6644 |
(message "helm-follow-mode not allowed in this source") |
|
6645 |
;; Make follow attr persistent for this emacs session. |
|
6646 |
(helm-follow-mode-set-source |
|
6647 |
(if (or enabled (and (numberp arg) (< arg 0))) -1 1) |
|
6648 |
src) |
|
6649 |
;; When arg is nil assume the call is interactive. |
|
6650 |
;; However if user call helm-follow-mode with a prefix arg, |
|
6651 |
;; the call will be considered non--interactive and |
|
6652 |
;; src-name will NOT be saved to helm-source-names-using-follow. |
|
6653 |
;; When called from lisp (non--interactive) src-name |
|
6654 |
;; will never be saved. |
|
6655 |
(when (and helm-follow-mode-persistent (null arg)) |
|
6656 |
(if (null enabled) |
|
6657 |
(unless (member name helm-source-names-using-follow) |
|
6658 |
(push name helm-source-names-using-follow) |
|
6659 |
(customize-save-variable 'helm-source-names-using-follow |
|
6660 |
helm-source-names-using-follow)) |
|
6661 |
(when (member name helm-source-names-using-follow) |
|
6662 |
(setq helm-source-names-using-follow |
|
6663 |
(delete name helm-source-names-using-follow)) |
|
6664 |
(customize-save-variable 'helm-source-names-using-follow |
|
6665 |
helm-source-names-using-follow)))) |
|
6666 |
(message "helm-follow-mode is %s" |
|
6667 |
(if (helm-follow-mode-p src) |
|
6668 |
"enabled" "disabled")) |
|
6669 |
(helm-display-mode-line src t))) |
|
6670 |
(message "Not enough candidates for helm-follow-mode")))))) |
|
6671 |
(put 'helm-follow-mode 'helm-only t) |
|
6672 |
|
|
6673 |
(defun helm-follow-execute-persistent-action-maybe (&optional delay) |
|
6674 |
"Execute persistent action in mode `helm-follow-mode'. |
|
6675 |
|
|
6676 |
This happen after: DELAY or the 'follow-attr value of current source |
|
6677 |
or `helm-follow-input-idle-delay' or `helm-input-idle-delay' secs." |
|
6678 |
(let* ((src (helm-get-current-source)) |
|
6679 |
(at (or delay |
|
6680 |
(assoc-default 'follow-delay src) |
|
6681 |
helm-follow-input-idle-delay |
|
6682 |
(or (and helm-input-idle-delay |
|
6683 |
(max helm-input-idle-delay 0.01)) |
|
6684 |
0.01)))) |
|
6685 |
(when (and (not (get-buffer-window helm-action-buffer 'visible)) |
|
6686 |
(not (helm-pos-header-line-p)) |
|
6687 |
(or (helm-follow-mode-p src) |
|
6688 |
(and helm-follow-mode-persistent |
|
6689 |
(member (assoc-default 'name src) |
|
6690 |
helm-source-names-using-follow))) |
|
6691 |
(null (eq (assoc-default 'follow src) 'never)) |
|
6692 |
(helm-get-selection nil nil src)) |
|
6693 |
(helm-follow-mode-set-source 1 src) |
|
6694 |
(run-with-idle-timer at nil (lambda () |
|
6695 |
(when helm-alive-p |
|
6696 |
(helm-execute-persistent-action))))))) |
|
6697 |
|
|
6698 |
(defun helm-follow-mode-p (&optional source) |
|
6699 |
(with-helm-buffer |
|
6700 |
(eq (helm-attr 'follow (or source (helm-get-current-source))) 1))) |
|
6701 |
|
|
6702 |
(defun helm-follow-mode-set-source (value &optional source) |
|
6703 |
(with-helm-buffer |
|
6704 |
(helm-attrset 'follow value (or source (helm-get-current-source))))) |
|
6705 |
|
|
6706 |
;;; Auto-resize mode |
|
6707 |
;; |
|
6708 |
(defun helm--autoresize-hook (&optional max-height min-height) |
|
6709 |
(when (helm-window) |
|
6710 |
(with-helm-window |
|
6711 |
(fit-window-to-buffer nil |
|
6712 |
(/ (* (frame-height) |
|
6713 |
(or max-height helm-autoresize-max-height)) |
|
6714 |
100) |
|
6715 |
(/ (* (frame-height) |
|
6716 |
(or min-height helm-autoresize-min-height)) |
|
6717 |
100))))) |
|
6718 |
|
|
6719 |
(define-minor-mode helm-autoresize-mode |
|
6720 |
"Auto resize helm window when enabled. |
|
6721 |
Helm window is re-sized according to `helm-autoresize-max-height' |
|
6722 |
and `helm-autoresize-min-height'. Note that when this mode is |
|
6723 |
enabled, helm behaves as if `helm-always-two-windows' is |
|
6724 |
enabled. |
|
6725 |
|
|
6726 |
See `fit-window-to-buffer' for more infos." |
|
6727 |
:group 'helm |
|
6728 |
:global t |
|
6729 |
(if helm-autoresize-mode |
|
6730 |
(progn (add-hook 'helm-after-update-hook 'helm--autoresize-hook) |
|
6731 |
(add-hook 'helm-window-configuration-hook 'helm--autoresize-hook)) |
|
6732 |
(remove-hook 'helm-after-update-hook 'helm--autoresize-hook) |
|
6733 |
(remove-hook 'helm-window-configuration-hook 'helm--autoresize-hook))) |
|
6734 |
|
|
6735 |
(defun helm-help () |
|
6736 |
"Generate helm's help according to `help-message' attribute. |
|
6737 |
|
|
6738 |
If `helm-buffer' is empty, provide completions on `helm-sources' to |
|
6739 |
choose its local documentation. |
|
6740 |
If source doesn't have any `help-message' attribute, a generic message |
|
6741 |
explaining this is added instead. |
|
6742 |
The global `helm-help-message' is always added after this local help." |
|
6743 |
(interactive) |
|
6744 |
(with-helm-alive-p |
|
6745 |
(let ((source (or (helm-get-current-source) |
|
6746 |
(helm-comp-read |
|
6747 |
"Help for: " |
|
6748 |
(cl-loop for src in (with-helm-buffer helm-sources) |
|
6749 |
for src-val = (if (symbolp src) |
|
6750 |
(symbol-value src) |
|
6751 |
src) |
|
6752 |
collect `(,(assoc-default 'name src-val) . |
|
6753 |
,src)) |
|
6754 |
:allow-nest t |
|
6755 |
:exec-when-only-one t)))) |
|
6756 |
(save-selected-window |
|
6757 |
(helm-help-internal |
|
6758 |
"*Helm Help*" |
|
6759 |
(lambda () |
|
6760 |
(helm-aif (assoc-default 'help-message source) |
|
6761 |
(insert (substitute-command-keys |
|
6762 |
(helm-interpret-value it))) |
|
6763 |
(insert "* No specific help for this source available.")) |
|
6764 |
(insert "\n\n" |
|
6765 |
(substitute-command-keys |
|
6766 |
(helm-interpret-value helm-help-message))))))))) |
|
6767 |
(put 'helm-help 'helm-only t) |
|
6768 |
|
|
6769 |
(defun helm-toggle-truncate-line () |
|
6770 |
"Toggle `truncate-lines' value in `helm-buffer'" |
|
6771 |
(interactive) |
|
6772 |
(with-helm-alive-p |
|
6773 |
(with-helm-buffer |
|
6774 |
(setq truncate-lines (not truncate-lines)) |
|
6775 |
(when (helm-get-previous-header-pos) |
|
6776 |
(helm-update (regexp-quote (helm-get-selection nil t))))))) |
|
6777 |
(put 'helm-toggle-truncate-line 'helm-only t) |
|
6778 |
|
|
6779 |
(provide 'helm) |
|
6780 |
|
|
6781 |
;; Local Variables: |
|
6782 |
;; byte-compile-warnings: (not obsolete) |
|
6783 |
;; coding: utf-8 |
|
6784 |
;; indent-tabs-mode: nil |
|
6785 |
;; End: |
|
6786 |
|
|
6787 |
;;; helm.el ends here |