home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / misc / search-completions.el < prev    next >
Encoding:
Text File  |  1993-05-24  |  17.8 KB  |  473 lines

  1. ;; search-completions
  2. ;; Description:  runs isearch in *Completions* buffer, and returns the
  3. ;;               completion point is on when the isearch terminates.
  4. ;;               Narrows completions interactively with regexp matches.
  5. ;; Author:    Radey Shouman              <rshouman@chpc.utexas.edu>
  6. ;; File:      search-completions.el
  7. ;; $modified: Fri May 21 17:21:24 1993 by rshouman $
  8. ;;
  9. ;; This program is free software; you can redistribute it and/or modify
  10. ;; it under the terms of the GNU General Public License as published by
  11. ;; the Free Software Foundation; either version 2 of the License, or
  12. ;; (at your option) any later version.
  13.  
  14. ;; This program is distributed in the hope that it will be useful,
  15. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. ;; GNU General Public License for more details.
  18.  
  19. ;; You should have received a copy of the GNU General Public License
  20. ;; along with this program; if not, write to the Free Software
  21. ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  
  23. ;; LCD Archive Entry:
  24. ;; search-completions|Radey Shouman|rshouman@chpc.utexas.edu|
  25. ;; Use isearch to select a completion, narrow completions with regexps.|
  26. ;; 21-May-1993|1.01|~/misc/search-completions.el.Z|
  27.  
  28. ;; INSTALLATION:                         
  29. ;; byte-compile it, then                 
  30. ;;                                      
  31. ;;(autoload 'isearch-completions "search-completions" 
  32. ;;      "search completions buffer" t) 
  33. ;;(autoload 'narrow-completions "search-completions" 
  34. ;;      "narrow completions buffer with a regexp" t)
  35. ;;(autoload 'completions-scroll-up "search-completions")
  36. ;;(autoload 'completions-scroll-down "search-completions")
  37. ;;(autoload 'completions-scroll-up-1 "search-completions")
  38. ;;(autoload 'completions-scroll-down-1 "search-completions")
  39. ;;(let ((map-list (list minibuffer-local-completion-map
  40. ;;              minibuffer-local-must-match-map))
  41. ;;      map)
  42. ;;  (while map-list
  43. ;;    (setq map (car map-list))
  44. ;;    (setq map-list (cdr map-list))
  45. ;;    (define-key map "\C-v" 'completions-scroll-up)
  46. ;;    (define-key map "\M-v" 'completions-scroll-down)
  47. ;;    (define-key map "\C-n" 'completions-scroll-up-1)
  48. ;;    (define-key map "\C-p" 'completions-scroll-down-1)
  49. ;;    (define-key map "\C-s" 'isearch-completions)
  50. ;;    (define-key map "\C-x\C-n" 'narrow-completions)
  51. ;;    (define-key map "\C-x\C-w" 'widen-completions)
  52. ;;    (define-key map "\C-g" 'narrow-completions-quit)))
  53. ;;
  54. ;;      or
  55. ;; 
  56. ;; (load "search-completions")
  57. ;;
  58. ;; This will bind your keys, unless you bind the variable
  59. ;; search-completions-dont-bind-my-keys to something non-nil.
  60. ;;
  61. ;; If you want to change the isearch exit character just for 
  62. ;; isearch-completions, set the variable isearch-completions-exit-char to
  63. ;; the character you want (I like 13, RET).
  64. ;; 
  65. ;; If you want to exit the minibuffer as soon as you exit the search, set
  66. ;; the variable isearch-completions-exit to t.  This will have an effect
  67. ;; during filename completion only if the filename in the minibuffer is
  68. ;; not a directory.
  69. ;;
  70. ;; This function uses a recursive minibuffer to read the regexp.
  71. ;; If enable-recursive-minibuffers is nil, then
  72. ;;
  73. ;; (setq narrow-completions-enable t)
  74. ;;
  75. ;; will allow the use of a recursive minibuffer for this function only.
  76. ;; If anyone ever autoloads this in a site-init.el, I would recommend that
  77. ;; narrow-completions-enable be t, but that narrow-completions be disabled
  78. ;; by default.
  79. ;;
  80. ;; USE:
  81. ;; isearch-completions:
  82. ;; To search through the completions buffer using isearch, type C-s in the
  83. ;; minibuffer window, then use the normal isearch commands to find the
  84. ;; completion you want.  If you exit the search with ESC, the completion
  85. ;; near point will be put in the minibuffer, if it is the sole completion,
  86. ;; the minibuffer will be exited with that value read.  If you decide that
  87. ;; you don't want to grab a value from the completions buffer, exit the search
  88. ;; using a random control character, the minibuffer will then be selected
  89. ;; without changing its contents.
  90. ;;
  91. ;; narrow-completions:
  92. ;; To narrow the possible completions with a regular expression, type 
  93. ;; C-xC-n in the minibuffer, then type in a regular expression.
  94. ;; This will cause only completions matching the regular expression to
  95. ;; be considered.  You can recursively narrow the search by typing 
  96. ;; C-xC-n again, or widen the completion table again by typing C-xC-w.
  97. ;; Using a prefix arg means that only completions *not* matching the regexp
  98. ;; will be considered.
  99. ;; 
  100. ;; When doing file name completion, subdirectories will be displayed as 
  101. ;; completions whether they match the current regexp(s) or not.
  102. ;;
  103. ;; For example, to see all variables with both "buffer" and "window" in
  104. ;; their names:
  105. ;; C-hvC-xC-nbuffer\RETC-xC-nwindow\RET\TAB
  106. ;;
  107. ;; To visit a file with a ".c" or ".h" extension:
  108. ;; C-xC-fC-n\.[ch]\RET\SPACE
  109. ;; 
  110. ;; This package also defines the functions read-directory-name and
  111. ;; read-file-name-narrowed, for reading file names restricted by a 
  112. ;; regexp.  See the documentation of those functions for an explanation
  113. ;; of how to use them.
  114. ;; 
  115.  
  116. ;; BUGS:
  117. ;; If minibuffer-completion-table is a symbol, i.e. programmed completion,
  118. ;; isearch-completions and narrow-completions assume they're doing filename
  119. ;; completion.  Programmed completion for some other purpose would probably 
  120. ;; not work very well with this package.
  121.  
  122. ;; CHANGES:
  123. ;; 21-5-93:
  124. ;;   Added scrolling functions, cleaned up key-definitions a bit.
  125. ;; 8-4-93:
  126. ;;   Fixed bug causing isearch-completions to fail for alists if the
  127. ;;   string found was not the sole completion by replacing try-completion
  128. ;;   with assoc.
  129. ;; 1-4-93:
  130. ;;   Fixed up key definitions to work with gmhist, suggestion of 
  131. ;;   Bill Benedetto <benedett@gentire.com>.
  132. ;;   Changed function called in isearch-completions to try to create
  133. ;;   a completions window to minibuffer-complete from minibuffer-complete-word.
  134. ;; 27-3-93:
  135. ;;   Added narrow-completions.
  136. ;; 21-3-93:
  137. ;;   Made isearch-completions exit minibuffer if isearch-completions-option
  138. ;;   is non-nil, added isearch-completions-char.
  139. ;; 20-3-93:
  140. ;;   Added test to see if isearch was terminated "normally".
  141. ;;   Removed interactive call, on advice from Dan LaLiberte.
  142. ;; 11-3-93:
  143. ;;   Changed (erase-buffer) in minibuffer to (zap-to-char -1 ?/)
  144. ;;     if completing on file names, after suggestion from Antonio DeSimone.
  145. ;;   Added test for proper completion when minibuffer-completion-table is
  146. ;;     and alist, fixed up code to do the right thing even if completions
  147. ;;     have imbedded spaces, after suggestion from Shiono Junichi.
  148.  
  149. (defvar isearch-completions-exit-char nil
  150.   "*Character to exit incremental search of the completions buffer, nil
  151. means use search-exit-char.  See also the variable search-exit-char. ")
  152.  
  153. (defvar isearch-completions-exit nil
  154.   "*If non-nil, exititing incremental search of the completions buffer also
  155. exits the minibuffer.  This will have an effect during filename completion
  156. only if the filename in the minibuffer is  not a directory. ")
  157.  
  158. (defvar narrow-completions-depth 0
  159.   "The number of times has narrow-completions has been called for this
  160. completing-read. ")
  161.  
  162. (defvar narrow-completions-enable nil
  163.   "Enable recursive minibuffers for the function narrow-completions,
  164. whether enable-recursive-minibuffers is true or not.  See the variable
  165. enable-recursive-minibuffers. ")
  166.  
  167. (defconst narrow-completions-dir-completions '("../" "./")
  168.   "*Completions that will be returned for a complete directory name. ")
  169.  
  170. (defun isearch-completions ()
  171.   "Do an isearch in the *Completions* buffer, if the isearch is terminated
  172. by typing isearch-exit-char (normally ESC), then whatever is near point is
  173. inserted in the minibuffer.  If the isearch is terminated by typing a random
  174. control character, the minibuffer contents are not changed. "
  175.   (interactive)
  176.   (let ((currwin (selected-window))
  177.     (compwin (get-buffer-window " *Completions*"))
  178.     found)
  179.                     ; If there isn't a completions buffer,
  180.                     ; make one.
  181.     (or compwin
  182.     (progn
  183.       (minibuffer-complete)
  184.       (setq compwin (get-buffer-window " *Completions*"))))
  185.     (if (null compwin)
  186.     nil
  187.       (let ((search-exit-char (or isearch-completions-exit-char
  188.                   search-exit-char))
  189.         (search-exit-option (or search-exit-option
  190.                     isearch-completions-exit)))
  191.     (unwind-protect
  192.         (progn
  193.           (select-window compwin)
  194.           (and (bobp) (forward-line 1))
  195.           (isearch-forward)
  196.           (setq unread-command-char -1)
  197.           (setq found (isearch-completions-grab)))
  198.       (select-window currwin))
  199.     (if (= last-input-char search-exit-char)
  200.         ;; If it's programmed completion, assume filename completion.
  201.         (if (symbolp minibuffer-completion-table)
  202.         (progn
  203.           (zap-to-char -1 ?/)
  204.           (insert found)
  205.           (if (and isearch-completions-exit
  206.                (not (string= 
  207.                  (file-name-directory found) found)))
  208.               (exit-minibuffer)
  209.             (minibuffer-complete-word)))
  210.           (erase-buffer)
  211.           (insert found)
  212.           (if isearch-completions-exit
  213.           (exit-minibuffer))))))))
  214.  
  215. (defun isearch-completions-grab ()
  216.   (save-excursion
  217.     (let* ((opoint (point))
  218.        (start (progn (skip-chars-backward "^ \n\t")
  219.              (point)))
  220.        (end (progn (skip-chars-forward "^ \n\t")
  221.                (point)))
  222.        (found (buffer-substring start end))
  223.        (table minibuffer-completion-table)
  224.        (pred minibuffer-completion-predicate))
  225.  
  226.       (if (not (listp table))
  227.       found
  228.     (if (assoc found table)
  229.         found
  230.       (debug)
  231.       (setq start (progn (beginning-of-line) (point)))
  232.       (setq found "")
  233.       (while (not (assoc found table))
  234.         (or (forward-word 1) (error "end of buffer!"))
  235.         (setq found (buffer-substring start (point))))
  236.       (if (>= (point) opoint)
  237.           found
  238.         (skip-chars-forward " \t")
  239.         (setq start (point))
  240.         (setq found "")
  241.         (while (not (assoc found table))
  242.           (or (forward-word 1) (error "end of buffer!"))
  243.           (setq found (buffer-substring start (point))))
  244.         found))))))
  245.  
  246. (defun completions-scroll-up (&optional arg)
  247.   "If the completions window is displayed, scroll it upward ARG lines. "
  248.   (interactive "P")
  249.   (save-excursion
  250.     (let ((currwin (selected-window))
  251.       (compwin (get-buffer-window " *Completions*")))
  252.       (if compwin
  253.       (unwind-protect
  254.           (progn
  255.         (select-window compwin)
  256.         (scroll-up (if arg (prefix-numeric-value arg))))
  257.         (select-window currwin))))))
  258.  
  259.  
  260. (defun completions-scroll-down (&optional arg)
  261.   "If the completions window is displayed, scroll it downward ARG lines. "
  262.   (interactive "p")
  263.   (save-excursion
  264.     (let ((currwin (selected-window))
  265.       (compwin (get-buffer-window " *Completions*")))
  266.       (if compwin
  267.       (unwind-protect
  268.           (progn
  269.         (select-window compwin)
  270.         (scroll-down (if arg (prefix-numeric-value arg))))
  271.         (select-window currwin))))))
  272.  
  273.  
  274. (defun completions-scroll-down-1 ()
  275.   "If the completions window is displayed, scroll it downward 1 line. "
  276.   (interactive)
  277.   (completions-scroll-down 1))
  278.  
  279.  
  280. (defun completions-scroll-up-1 ()
  281.   "If the completions window is displayed, scroll it upward 1 line. "
  282.   (interactive)
  283.   (completions-scroll-up 1))
  284.  
  285.  
  286. (defun narrow-completions (regexp &optional complement)
  287.   "Restrict possible completions to those matching REGEXP.  With optional
  288. COMPLEMENT non-nil or interactive prefix arg, restrict completions to those
  289. not matching REGEXP.  If file name completion is being done, include
  290. subdirectories also. This function should be called from the minibuffer. "
  291.   (interactive
  292.    (let ((enable-recursive-minibuffers (or enable-recursive-minibuffers
  293.                        narrow-completions-enable)))
  294.      (list (read-string "regexp: ")
  295.        (if current-prefix-arg t nil))))
  296.   (let ((narrow-completions-depth (1+ narrow-completions-depth))
  297.     (minibuffer-completion-table minibuffer-completion-table)
  298.     (minibuffer-completion-predicate minibuffer-completion-predicate))
  299.     ;; If it's programmed completion, assume filename completion.
  300.     (if (symbolp minibuffer-completion-table)
  301.     (progn
  302.       (setq minibuffer-completion-predicate
  303.         (cons regexp
  304.               (cons complement
  305.                 (cons minibuffer-completion-table
  306.                   minibuffer-completion-predicate))))
  307.       (setq minibuffer-completion-table 'narrow-completions-internal)
  308.       (if (get-buffer-window " *Completions*")
  309.           (minibuffer-complete-word))
  310.       (recursive-edit))
  311.       (if (or
  312.        (listp minibuffer-completion-table)
  313.        (arrayp minibuffer-completion-table))
  314.       (let ((completions 
  315.          (all-completions "" minibuffer-completion-table
  316.                   minibuffer-completion-predicate)))
  317.         (setq minibuffer-completion-table (mapcar 'list completions))
  318.         (setq minibuffer-completion-predicate
  319.           (if complement
  320.               (` (lambda (arg)
  321.                (not (string-match (, regexp) (car arg)))))
  322.             (` (lambda (arg)
  323.              (string-match (, regexp) (car arg))))))
  324.         (if (get-buffer-window " *Completions*")
  325.         (minibuffer-complete))
  326.         (recursive-edit))
  327.     (error "Cannot narrow completions for this table. ")))
  328.     (setq unread-command-char last-input-char)))
  329.  
  330. (defun widen-completions ()
  331.   (interactive)
  332.   (if (zerop narrow-completions-depth)
  333.       (message "Completions not narrowed")
  334.     (setq last-input-char
  335.       (if (get-buffer-window " *Completions*")
  336.           (if (symbolp minibuffer-completion-table)
  337.           32
  338.         ?\t)
  339.         -1))
  340.     (exit-recursive-edit)))
  341.  
  342. (defun narrow-completions-quit ()
  343.   "Quit from this narrowed completing-read, and all those that called it. "
  344.   (interactive)
  345.   (if (zerop narrow-completions-depth)
  346.       (abort-recursive-edit)
  347.     (exit-recursive-edit)))
  348.  
  349. ;; PREDICATE here has the form:
  350. ;; (regexp complement original-table . original-predicate)
  351. ;; where regexp is the narrowing regexp, and complement is t if we
  352. ;; want to exclude matches to this regexp.
  353. ;; original-table and original-predicate are the arguments that
  354. ;; were passed to the completion-table calling this one.
  355. ;; original-table should thus be either 'read-file-name-internal, or
  356. ;; 'narrow-completions-internal for multiple narrowings.
  357. (defun narrow-completions-internal (string predicate action)
  358.   "Completion table function for narrow-completions, called only
  359. for narrowing programmed completion. "
  360.   (let ((regexp (car (nthcdr 0 predicate)))
  361.     (complement (car (nthcdr 1 predicate)))
  362.     (table (car (nthcdr 2 predicate)))
  363.     (predicate (nthcdr 3 predicate)))
  364.     (if (eq action 'lambda)
  365.     (and (try-completion string table predicate)
  366.          (or
  367.           (if regexp
  368.           (if complement
  369.               (not (string-match regexp string))
  370.             (string-match regexp string)))
  371.           (string= string (file-name-directory string)))
  372.          t)
  373.       (let ((completions (all-completions string table predicate))
  374.         (fn (if regexp
  375.             (if complement
  376.             (function
  377.              (lambda (arg)
  378.                (if (or (not (string-match regexp arg))
  379.                    (string= arg (file-name-directory arg)))
  380.                    arg)))
  381.               (function 
  382.                (lambda (arg)
  383.              (if (or (string-match regexp arg)
  384.                  (string= arg (file-name-directory arg)))
  385.                  arg))))
  386.           (function (lambda (arg)
  387.                   (if (string= arg (file-name-directory arg))
  388.                   arg))))))
  389.     (setq completions
  390.           (delq nil (mapcar fn completions)))
  391.     (if action
  392.         completions
  393.       (let* ((subdir (file-name-directory string))
  394.         (name (file-name-nondirectory string))
  395.         (completion
  396.          (try-completion name (mapcar 'list completions))))
  397.         (if (or (eq completion t)
  398.             (and
  399.              (subset completions narrow-completions-dir-completions)
  400.              (subset narrow-completions-dir-completions completions)))
  401.         string
  402.           (concat (or subdir "") completion))))))))
  403.  
  404. ;; Stolen from the tree-dired distribution.
  405. (or (fboundp 'member)
  406.     (defun member (x y)
  407.       "Like memq, but uses `equal' for comparison.
  408. This is a subr in Emacs 19."
  409.       (while (and y (not (equal x (car y))))
  410.     (setq y (cdr y)))
  411.       y))
  412.  
  413. (defun subset (set1 set2)
  414.   "Returns t if list SET1 is a subset of list SET2, nil otherwise. 
  415. Membership is tested with member. "
  416.   (catch 'exit
  417.     (mapcar (function (lambda (arg)
  418.             (or (member arg set2)
  419.                 (throw 'exit nil))))
  420.         set1)
  421.     t))
  422.  
  423. ;; Might as well define these, they might be handy some day.
  424.  
  425. (defun read-file-name-narrowed (prompt dir default mustmatch
  426.                        regexp &optional complement)
  427.   "Read file name, prompting with PROMPT and completing in directory DIR.
  428. Value is not expanded!  You must call expand-file-name yourself.
  429. Default name to DEFAULT if user enters a null string.
  430. Fourth arg MUSTMATCH non-nil means require existing file's name.
  431. Non-nil and non-t means also require confirmation after completion.
  432. DIR defaults to current buffer's directory default.  Offer as completions
  433. only file namess matching REGEXP, or directories, with optional COMPLEMENT,
  434. offer only file names not matching REGEXP. "
  435.   (let ((predicate (append (list regexp complement)
  436.                (cons 'read-file-name-internal dir))))
  437.     (completing-read prompt 'narrow-completions-internal
  438.              predicate mustmatch default)))
  439.  
  440. (defun read-directory-name (prompt dir default mustmatch)
  441.   "Read directory name, prompting with PROMPT and completing in directory DIR.
  442. Value is not expanded!  You must call expand-file-name yourself.
  443. Default name to DEFAULT if user enters a null string.
  444. Fourth arg MUSTMATCH non-nil means require existing file's name.
  445. Non-nil and non-t means also require confirmation after completion.
  446. DIR defaults to current buffer's directory default. "
  447.   (read-file-name-narrowed prompt dir default mustmatch nil))
  448.  
  449. ;; Add key definitions.
  450. (if (and (boundp 'search-completions-dont-bind-my-keys)
  451.      search-completions-dont-bind-my-keys)
  452.     nil
  453.   (let ((map-list (list minibuffer-local-completion-map
  454.             minibuffer-local-must-match-map))
  455.     map)
  456.     (if (boundp 'gmhist-completion-map)
  457.     (setq map-list (append map-list
  458.                    '(gmhist-completion-map
  459.                  gmhist-must-match-map))))
  460.     (while map-list
  461.       (setq map (car map-list))
  462.       (setq map-list (cdr map-list))
  463.  
  464.       (define-key map "\C-v" 'completions-scroll-up)
  465.       (define-key map "\M-v" 'completions-scroll-down)
  466.       (define-key map "\C-n" 'completions-scroll-up-1)
  467.       (define-key map "\C-p" 'completions-scroll-down-1)
  468.       (define-key map "\C-s" 'isearch-completions)
  469.       (define-key map "\C-x\C-n" 'narrow-completions)
  470.       (define-key map "\C-x\C-w" 'widen-completions)
  471.       (define-key map "\C-g" 'narrow-completions-quit))))
  472.  
  473.