1;;; em-cmpl.el --- completion using the TAB key
2
3;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004,
4;;   2005, 2006, 2007 Free Software Foundation, Inc.
5
6;; Author: John Wiegley <johnw@gnu.org>
7
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation; either version 2, or (at your option)
13;; any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
21;; along with GNU Emacs; see the file COPYING.  If not, write to the
22;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23;; Boston, MA 02110-1301, USA.
24
25(provide 'em-cmpl)
26
27(eval-when-compile (require 'esh-maint))
28(require 'esh-util)
29
30(defgroup eshell-cmpl nil
31  "This module provides a programmable completion function bound to
32the TAB key, which allows for completing command names, file names,
33variable names, arguments, etc."
34  :tag "Argument completion"
35  :group 'eshell-module)
36
37;;; Commentary:
38
39;; Eshell, by using the pcomplete package, provides a full
40;; programmable completion facility that is comparable to shells like
41;; tcsh or zsh.
42;;
43;; Completions are context-sensitive, which means that pressing <TAB>
44;; after the command 'rmdir' will result in a list of directories,
45;; while doing so after 'rm' will result in a list of all file
46;; entries.
47;;
48;; Many builtin completion rules are provided, for commands such as
49;; `cvs', or RedHat's `rpm' utility.  Adding new completion rules is
50;; no more difficult than writing a plain Lisp functions, and they can
51;; be debugged, profiled, and compiled using exactly the same
52;; facilities (since in fact, they *are* just Lisp functions).  See
53;; the definition of the function `pcomplete/make' for an example of
54;; how to write a completion function.
55;;
56;; The completion facility is very easy to use.  Just press TAB.  If
57;; there are a large number of possible completions, a buffer will
58;; appear showing a list of them.  Completions may be selected from
59;; that buffer using the mouse.  If no completion is selected, and the
60;; user starts doing something else, the display buffer will
61;; automatically disappear.
62;;
63;; If the list of possible completions is very small, Eshell will
64;; "cycle" through them, selecting a different entry each time <TAB>
65;; is pressed.  <S-TAB> may be used to cycle in the opposite
66;; direction.
67;;
68;; Glob patterns can also be cycled.  For example, entering 'echo
69;; x*<tab>' will cycle through all the filenames beginning with 'x'.
70;; This is done because the glob list is treated as though it were a
71;; list of possible completions.  Pressing <C-c SPC> will insert all
72;; of the matching glob patterns at point.
73;;
74;; If a Lisp form is being entered, <TAB> will complete the Lisp
75;; symbol name, in exactly the same way that <M-TAB> does in Emacs
76;; Lisp mode.
77;;
78;; The list of possible completions can be viewed at any point by
79;; pressing <M-?>.
80;;
81;; Finally, context-related help can be accessed by pressing <C-c i>.
82;; This only works well if the completion function has provided Eshell
83;; with sufficient pointers to locate the relevant help text.
84
85;;; User Variables:
86
87(defcustom eshell-cmpl-load-hook '(eshell-cmpl-initialize)
88  "*A list of functions to run when `eshell-cmpl' is loaded."
89  :type 'hook
90  :group 'eshell-cmpl)
91
92(defcustom eshell-show-lisp-completions nil
93  "*If non-nil, include Lisp functions in the command completion list.
94If this variable is nil, Lisp completion can still be done in command
95position by using M-TAB instead of TAB."
96  :type 'boolean
97  :group 'eshell-cmpl)
98
99(defcustom eshell-show-lisp-alternatives t
100  "*If non-nil, and no other completions found, show Lisp functions.
101Setting this variable means nothing if `eshell-show-lisp-completions'
102is non-nil."
103  :type 'boolean
104  :group 'eshell-cmpl)
105
106(defcustom eshell-no-completion-during-jobs t
107  "*If non-nil, don't allow completion while a process is running."
108  :type 'boolean
109  :group 'eshell-cmpl)
110
111(defcustom eshell-command-completions-alist
112  '(("acroread" . "\\.pdf\\'")
113    ("xpdf"     . "\\.pdf\\'")
114    ("ar"       . "\\.[ao]\\'")
115    ("gcc"      . "\\.[Cc]\\([Cc]\\|[Pp][Pp]\\)?\\'")
116    ("g++"      . "\\.[Cc]\\([Cc]\\|[Pp][Pp]\\)?\\'")
117    ("cc"       . "\\.[Cc]\\([Cc]\\|[Pp][Pp]\\)?\\'")
118    ("CC"       . "\\.[Cc]\\([Cc]\\|[Pp][Pp]\\)?\\'")
119    ("acc"      . "\\.[Cc]\\([Cc]\\|[Pp][Pp]\\)?\\'")
120    ("bcc"      . "\\.[Cc]\\([Cc]\\|[Pp][Pp]\\)?\\'")
121    ("readelf"  . "\\(\\`[^.]*\\|\\.\\([ao]\\|so\\)\\)\\'")
122    ("objdump"  . "\\(\\`[^.]*\\|\\.\\([ao]\\|so\\)\\)\\'")
123    ("nm"       . "\\(\\`[^.]*\\|\\.\\([ao]\\|so\\)\\)\\'")
124    ("gdb"      . "\\`\\([^.]*\\|a\\.out\\)\\'")
125    ("dbx"      . "\\`\\([^.]*\\|a\\.out\\)\\'")
126    ("sdb"      . "\\`\\([^.]*\\|a\\.out\\)\\'")
127    ("adb"      . "\\`\\([^.]*\\|a\\.out\\)\\'"))
128  "*An alist that defines simple argument type correlations.
129This is provided for common commands, as a simplistic alternative
130to writing a completion function."
131  :type '(repeat (cons string regexp))
132  :group 'eshell-cmpl)
133
134(defcustom eshell-cmpl-file-ignore "~\\'"
135  (documentation-property 'pcomplete-file-ignore
136			  'variable-documentation)
137  :type (get 'pcomplete-file-ignore 'custom-type)
138  :group 'eshell-cmpl)
139
140(defcustom eshell-cmpl-dir-ignore "\\`\\(\\.\\.?\\|CVS\\)/\\'"
141  (documentation-property 'pcomplete-dir-ignore
142			  'variable-documentation)
143  :type (get 'pcomplete-dir-ignore 'custom-type)
144  :group 'eshell-cmpl)
145
146(defcustom eshell-cmpl-ignore-case (eshell-under-windows-p)
147  (documentation-property 'pcomplete-ignore-case
148			  'variable-documentation)
149  :type (get 'pcomplete-ignore-case 'custom-type)
150  :group 'eshell-cmpl)
151
152(defcustom eshell-cmpl-autolist nil
153  (documentation-property 'pcomplete-autolist
154			  'variable-documentation)
155  :type (get 'pcomplete-autolist 'custom-type)
156  :group 'eshell-cmpl)
157
158(defcustom eshell-cmpl-suffix-list (list ?/ ?:)
159  (documentation-property 'pcomplete-suffix-list
160			  'variable-documentation)
161  :type (get 'pcomplete-suffix-list 'custom-type)
162  :group 'pcomplete)
163
164(defcustom eshell-cmpl-recexact nil
165  (documentation-property 'pcomplete-recexact
166			  'variable-documentation)
167  :type (get 'pcomplete-recexact 'custom-type)
168  :group 'eshell-cmpl)
169
170(defcustom eshell-cmpl-man-function 'man
171  (documentation-property 'pcomplete-man-function
172			  'variable-documentation)
173  :type (get 'pcomplete-man-function 'custom-type)
174  :group 'eshell-cmpl)
175
176(defcustom eshell-cmpl-compare-entry-function 'file-newer-than-file-p
177  (documentation-property 'pcomplete-compare-entry-function
178			  'variable-documentation)
179  :type (get 'pcomplete-compare-entry-function 'custom-type)
180  :group 'eshell-cmpl)
181
182(defcustom eshell-cmpl-expand-before-complete nil
183  (documentation-property 'pcomplete-expand-before-complete
184			  'variable-documentation)
185  :type (get 'pcomplete-expand-before-complete 'custom-type)
186  :group 'eshell-cmpl)
187
188(defcustom eshell-cmpl-cycle-completions t
189  (documentation-property 'pcomplete-cycle-completions
190			  'variable-documentation)
191  :type (get 'pcomplete-cycle-completions 'custom-type)
192  :group 'eshell-cmpl)
193
194(defcustom eshell-cmpl-cycle-cutoff-length 5
195  (documentation-property 'pcomplete-cycle-cutoff-length
196			  'variable-documentation)
197  :type (get 'pcomplete-cycle-cutoff-length 'custom-type)
198  :group 'eshell-cmpl)
199
200(defcustom eshell-cmpl-restore-window-delay 1
201  (documentation-property 'pcomplete-restore-window-delay
202			  'variable-documentation)
203  :type (get 'pcomplete-restore-window-delay 'custom-type)
204  :group 'eshell-cmpl)
205
206(defcustom eshell-command-completion-function
207  (function
208   (lambda ()
209     (pcomplete-here (eshell-complete-commands-list))))
210  (documentation-property 'pcomplete-command-completion-function
211			  'variable-documentation)
212  :type (get 'pcomplete-command-completion-function 'custom-type)
213  :group 'eshell-cmpl)
214
215(defcustom eshell-cmpl-command-name-function
216  'eshell-completion-command-name
217  (documentation-property 'pcomplete-command-name-function
218			  'variable-documentation)
219  :type (get 'pcomplete-command-name-function 'custom-type)
220  :group 'eshell-cmpl)
221
222(defcustom eshell-default-completion-function
223  (function
224   (lambda ()
225     (while (pcomplete-here
226	     (pcomplete-dirs-or-entries
227	      (cdr (assoc (funcall eshell-cmpl-command-name-function)
228			  eshell-command-completions-alist)))))))
229  (documentation-property 'pcomplete-default-completion-function
230			  'variable-documentation)
231  :type (get 'pcomplete-default-completion-function 'custom-type)
232  :group 'eshell-cmpl)
233
234(defcustom eshell-cmpl-use-paring t
235  (documentation-property 'pcomplete-use-paring 'variable-documentation)
236  :type (get 'pcomplete-use-paring 'custom-type)
237  :group 'eshell-cmpl)
238
239;;; Functions:
240
241(defun eshell-cmpl-initialize ()
242  "Initialize the completions module."
243  (unless (fboundp 'pcomplete)
244    (load "pcmpl-auto" t t))
245  (set (make-local-variable 'pcomplete-command-completion-function)
246       eshell-command-completion-function)
247  (set (make-local-variable 'pcomplete-command-name-function)
248       eshell-cmpl-command-name-function)
249  (set (make-local-variable 'pcomplete-default-completion-function)
250       eshell-default-completion-function)
251  (set (make-local-variable 'pcomplete-parse-arguments-function)
252       'eshell-complete-parse-arguments)
253  (set (make-local-variable 'pcomplete-file-ignore)
254       eshell-cmpl-file-ignore)
255  (set (make-local-variable 'pcomplete-dir-ignore)
256       eshell-cmpl-dir-ignore)
257  (set (make-local-variable 'pcomplete-ignore-case)
258       eshell-cmpl-ignore-case)
259  (set (make-local-variable 'pcomplete-autolist)
260       eshell-cmpl-autolist)
261  (set (make-local-variable 'pcomplete-suffix-list)
262       eshell-cmpl-suffix-list)
263  (set (make-local-variable 'pcomplete-recexact)
264       eshell-cmpl-recexact)
265  (set (make-local-variable 'pcomplete-man-function)
266       eshell-cmpl-man-function)
267  (set (make-local-variable 'pcomplete-compare-entry-function)
268       eshell-cmpl-compare-entry-function)
269  (set (make-local-variable 'pcomplete-expand-before-complete)
270       eshell-cmpl-expand-before-complete)
271  (set (make-local-variable 'pcomplete-cycle-completions)
272       eshell-cmpl-cycle-completions)
273  (set (make-local-variable 'pcomplete-cycle-cutoff-length)
274       eshell-cmpl-cycle-cutoff-length)
275  (set (make-local-variable 'pcomplete-restore-window-delay)
276       eshell-cmpl-restore-window-delay)
277  (set (make-local-variable 'pcomplete-use-paring)
278       eshell-cmpl-use-paring)
279  ;; `pcomplete-arg-quote-list' should only be set after all the
280  ;; load-hooks for any other extension modules have been run, which
281  ;; is true at the time `eshell-mode-hook' is run
282  (add-hook 'eshell-mode-hook
283	    (function
284	     (lambda ()
285	       (set (make-local-variable 'pcomplete-arg-quote-list)
286		    eshell-special-chars-outside-quoting))) nil t)
287  (add-hook 'pcomplete-quote-arg-hook 'eshell-quote-backslash nil t)
288  (define-key eshell-mode-map [(meta tab)] 'lisp-complete-symbol)
289  (define-key eshell-mode-map [(meta control ?i)] 'lisp-complete-symbol)
290  (define-key eshell-command-map [(meta ?h)] 'eshell-completion-help)
291  (define-key eshell-command-map [tab] 'pcomplete-expand-and-complete)
292  (define-key eshell-command-map [(control ?i)]
293    'pcomplete-expand-and-complete)
294  (define-key eshell-command-map [space] 'pcomplete-expand)
295  (define-key eshell-command-map [? ] 'pcomplete-expand)
296  (define-key eshell-mode-map [tab] 'pcomplete)
297  (define-key eshell-mode-map [(control ?i)] 'pcomplete)
298  ;; jww (1999-10-19): Will this work on anything but X?
299  (if (eshell-under-xemacs-p)
300      (define-key eshell-mode-map [iso-left-tab] 'pcomplete-reverse)
301    (define-key eshell-mode-map [(shift iso-lefttab)] 'pcomplete-reverse)
302    (define-key eshell-mode-map [(shift control ?i)] 'pcomplete-reverse))
303  (define-key eshell-mode-map [(meta ??)] 'pcomplete-list))
304
305(defun eshell-completion-command-name ()
306  "Return the command name, possibly sans globbing."
307  (let ((cmd (file-name-nondirectory (pcomplete-arg 'first))))
308    (setq cmd (if (and (> (length cmd) 0)
309		       (eq (aref cmd 0) eshell-explicit-command-char))
310		  (substring cmd 1)
311		cmd))
312    (if (eshell-under-windows-p)
313	(file-name-sans-extension cmd)
314      cmd)))
315
316(defun eshell-completion-help ()
317  (interactive)
318  (if (= (point) eshell-last-output-end)
319      (describe-prefix-bindings)
320    (call-interactively 'pcomplete-help)))
321
322(defun eshell-complete-parse-arguments ()
323  "Parse the command line arguments for `pcomplete-argument'."
324  (when (and eshell-no-completion-during-jobs
325	     (eshell-interactive-process))
326    (insert-and-inherit "\t")
327    (throw 'pcompleted t))
328  (let ((end (point-marker))
329	(begin (save-excursion (eshell-bol) (point)))
330	(posns (list t))
331	args delim)
332    (when (memq this-command '(pcomplete-expand
333			       pcomplete-expand-and-complete))
334      (run-hook-with-args 'eshell-expand-input-functions begin end)
335      (if (= begin end)
336	  (end-of-line))
337      (setq end (point-marker)))
338    (if (setq delim
339	      (catch 'eshell-incomplete
340		(ignore
341		 (setq args (eshell-parse-arguments begin end)))))
342	(cond ((memq (car delim) '(?\{ ?\<))
343	       (setq begin (1+ (cadr delim))
344		     args (eshell-parse-arguments begin end)))
345	      ((eq (car delim) ?\()
346	       (lisp-complete-symbol)
347	       (throw 'pcompleted t))
348	      (t
349	       (insert-and-inherit "\t")
350	       (throw 'pcompleted t))))
351    (when (get-text-property (1- end) 'comment)
352      (insert-and-inherit "\t")
353      (throw 'pcompleted t))
354    (let ((pos begin))
355      (while (< pos end)
356	(if (get-text-property pos 'arg-begin)
357	    (nconc posns (list pos)))
358	(setq pos (1+ pos))))
359    (setq posns (cdr posns))
360    (assert (= (length args) (length posns)))
361    (let ((a args)
362	  (i 0)
363	  l final)
364      (while a
365	(if (and (consp (car a))
366		 (eq (caar a) 'eshell-operator))
367	    (setq l i))
368	(setq a (cdr a) i (1+ i)))
369      (and l
370	   (setq args (nthcdr (1+ l) args)
371		 posns (nthcdr (1+ l) posns))))
372    (assert (= (length args) (length posns)))
373    (when (and args (eq (char-syntax (char-before end)) ? )
374	       (not (eq (char-before (1- end)) ?\\)))
375      (nconc args (list ""))
376      (nconc posns (list (point))))
377    (cons (mapcar
378	   (function
379	    (lambda (arg)
380	      (let ((val
381		     (if (listp arg)
382			 (let ((result
383				(eshell-do-eval
384				 (list 'eshell-commands arg) t)))
385			   (assert (eq (car result) 'quote))
386			   (cadr result))
387		       arg)))
388		(if (numberp val)
389		    (setq val (number-to-string val)))
390		(or val ""))))
391	   args)
392	  posns)))
393
394(defun eshell-complete-commands-list ()
395  "Generate list of applicable, visible commands."
396  (let ((filename (pcomplete-arg)) glob-name)
397    (if (file-name-directory filename)
398	(pcomplete-executables)
399      (if (and (> (length filename) 0)
400	       (eq (aref filename 0) eshell-explicit-command-char))
401	  (setq filename (substring filename 1)
402		pcomplete-stub filename
403		glob-name t))
404      (let* ((paths (split-string (getenv "PATH") path-separator))
405	     (cwd (file-name-as-directory
406		   (expand-file-name default-directory)))
407	     (path "") (comps-in-path ())
408	     (file "") (filepath "") (completions ()))
409	;; Go thru each path in the search path, finding completions.
410	(while paths
411	  (setq path (file-name-as-directory
412		      (expand-file-name (or (car paths) ".")))
413		comps-in-path
414		(and (file-accessible-directory-p path)
415		     (file-name-all-completions filename path)))
416	  ;; Go thru each completion found, to see whether it should
417	  ;; be used.
418	  (while comps-in-path
419	    (setq file (car comps-in-path)
420		  filepath (concat path file))
421	    (if (and (not (member file completions)) ;
422		     (or (string-equal path cwd)
423			 (not (file-directory-p filepath)))
424		     (file-executable-p filepath))
425		(setq completions (cons file completions)))
426	    (setq comps-in-path (cdr comps-in-path)))
427	  (setq paths (cdr paths)))
428	;; Add aliases which are currently visible, and Lisp functions.
429	(pcomplete-uniqify-list
430	 (if glob-name
431	     completions
432	   (setq completions
433		 (append (and (eshell-using-module 'eshell-alias)
434			      (funcall (symbol-function 'eshell-alias-completions)
435				       filename))
436			 (eshell-winnow-list
437			  (mapcar
438			   (function
439			    (lambda (name)
440			      (substring name 7)))
441			   (all-completions (concat "eshell/" filename)
442					    obarray 'functionp))
443			  nil '(eshell-find-alias-function))
444			 completions))
445	   (append (and (or eshell-show-lisp-completions
446			    (and eshell-show-lisp-alternatives
447				 (null completions)))
448			(all-completions filename obarray 'functionp))
449		   completions)))))))
450
451;;; Code:
452
453;;; arch-tag: 0e914699-673a-45f8-8cbf-82e1dbc571bc
454;;; em-cmpl.el ends here
455