home *** CD-ROM | disk | FTP | other *** search
- ;;;; prompt.jl -- Prompt in a buffer with completion
- ;;; Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
-
- ;;; This file is part of Jade.
-
- ;;; Jade is free software; you can redistribute it and/or modify it
- ;;; under the terms of the GNU General Public License as published by
- ;;; the Free Software Foundation; either version 2, or (at your option)
- ;;; any later version.
-
- ;;; Jade is distributed in the hope that it will be useful, but
- ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
- ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;;; GNU General Public License for more details.
-
- ;;; You should have received a copy of the GNU General Public License
- ;;; along with Jade; see the file COPYING. If not, write to
- ;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- (defvar prompt-keymap (make-keylist))
-
- (defvar prompt-buffer-list '()
- "Stack of buffers which can be used for prompts.")
-
- (bind-keys prompt-keymap
- "TAB" 'prompt-complete-word
- "RET" 'prompt-enter-line
- "LMB-CLICK2" 'prompt-select-completion
- "RMB-CLICK1" 'prompt-complete-word
- "Meta-?" 'prompt-print-word-completions
- "Ctrl-g" 'prompt-cancel)
-
-
- ;; Configuration variables
-
- (defvar prompt-completion-function nil
- "Optional function taking one argument, the string to be completed. It
- should return a list of all matches.")
-
- (defvar prompt-validate-function nil
- "Optional function taking one argument, the string which has been entered.
- Should return non-nil when this string may be accepted (and therefore the
- prompt will end). If it returns the symbol t the string is returned as-is,
- if some other non-nil value is returned *that* is the value returned by
- the prompt.")
-
- (defconst prompt-def-regexps ["." "^|$"]
- "Default value of `prompt-word-regexps'")
-
- (defvar prompt-word-regexps prompt-def-regexps
- "Vector of two regexps; the values of `word-regexp' and `word-not-regexp'
- for the prompt.")
-
- (defvar prompt-list nil
- "Used by the `prompt-complete-from-list' and `prompt-validate-from-list'
- to supply possible completions.")
-
- (defvar prompt-symbol-predicate nil
- "Predicate used when prompting for symbols.")
-
- (defvar amiga-use-file-req-p t
- "*AMIGA ONLY*
- When non-nil the normal ASL file requester is used when file names are
- prompted for.")
-
-
- (defvar prompt-buffer nil
- "The buffer being used for the prompt.")
-
- (defvar prompt-completions-pos nil
- "Position at which the list of completions should be printed.")
-
-
- ;; Main entrypoint
-
- (defun prompt2 (&optional title start)
- "Prompts for a string using completion. TITLE is the optional title to
- print in the buffer, START the original contents of the buffer.
- The string entered is returned, or nil if the prompt is cancelled (by Ctrl-g)."
- (let*
- (prompt-buffer
- prompt-line-pos
- prompt-completions-pos
- result)
- (if prompt-buffer-list
- (setq prompt-buffer (car prompt-buffer-list)
- prompt-buffer-list (cdr prompt-buffer-list))
- (setq prompt-buffer (make-buffer "*prompt*")))
- (setq buffer-list (cons prompt-buffer buffer-list))
- (set-buffer-special prompt-buffer t)
- (with-buffer prompt-buffer
- (setq word-regexp (aref prompt-word-regexps 0)
- word-not-regexp (aref prompt-word-regexps 1))
- (if (stringp title)
- (insert title)
- (insert "Enter string:"))
- (if (stringp start)
- (format (current-buffer) "\n\n%s\n\n" start)
- (insert "\n\n\n\n"))
- (insert "::Completions::\n")
- (setq prompt-completions-pos (cursor-pos))
- (goto-char (line-end (prev-line 3)))
- (setq keymap-path '(prompt-keymap global-keymap)
- buffer-undo-list nil
- result (catch 'prompt (recursive-edit))
- buffer-list (delq prompt-buffer buffer-list)))
- (clear-buffer prompt-buffer)
- (setq prompt-buffer-list (cons prompt-buffer prompt-buffer-list))
- result))
-
-
- ;; Subroutines
-
- (defun prompt-enter-line (&optional whole-line)
- (interactive)
- (let*
- ((pos (if (and (> (cursor-pos) prompt-completions-pos)
- whole-line)
- (line-end)
- (cursor-pos)))
- (line (copy-area (line-start) pos)))
- (if (or (not prompt-validate-function)
- (let
- ((res (funcall prompt-validate-function line)))
- (when (and res (not (eq res t)))
- (setq line res))
- res))
- (throw 'prompt line)
- (beep))))
-
- (defun prompt-select-completion ()
- (interactive)
- (goto-char (mouse-pos))
- (prompt-enter-line t))
-
- ;; Returns the number of completions found.
- (defun prompt-complete-word ()
- (interactive)
- (if (not prompt-completion-function)
- (progn
- (message "No completion in this prompt!")
- 0)
- (let*
- ((word-pos (or (word-start (left-char))
- (line-start)))
- (word (copy-area word-pos (cursor-pos)))
- (comp-list (funcall prompt-completion-function word))
- (num-found (length comp-list))
- (buffer-record-undo nil))
- (cond
- ((= num-found 0)
- (delete-area prompt-completions-pos (buffer-end))
- (message "No completions."))
- ((= num-found 1)
- (goto-char (replace-string word (car comp-list) word-pos))
- (delete-area prompt-completions-pos (buffer-end))
- (message "Unique completion."))
- (t
- (prompt-print-completions comp-list)
- (when (not (string-head-eq (car comp-list) word))
- ;; Completions don't match their source at all.
- (delete-area word-pos (cursor-pos))
- (setq word ""))
- (goto-char (replace-string word
- (make-completion-string word comp-list)
- word-pos))
- (format t "%d completions." num-found)))
- num-found)))
-
- (defun prompt-print-completions (comp-list)
- (let*
- ((ipos (copy-pos prompt-completions-pos))
- ;; Don't want to record undo information for the completion list
- (buffer-record-undo nil))
- (delete-area ipos (buffer-end))
- (insert "\n" ipos)
- (while (consp comp-list)
- (format (cons (current-buffer) ipos) "%s\n" (car comp-list))
- (setq comp-list (cdr comp-list)))))
-
- (defun prompt-print-word-completions ()
- (interactive)
- (prompt-print-completions
- (funcall prompt-comp-func
- (copy-area (or (word-start (left-char))
- (line-start))
- (cursor-pos)))))
-
- (defun prompt-cancel ()
- (interactive)
- (message "Quit!")
- (throw 'prompt nil))
-
-
- ;; Various completion/validation functions
-
- (defun prompt-complete-symbol (word)
- (mapcar 'symbol-name (apropos (concat ?^ word) prompt-symbol-predicate)))
-
- (defun prompt-validate-symbol (name)
- (and (find-symbol name)
- (or (not prompt-symbol-predicate)
- (funcall prompt-symbol-predicate (find-symbol name)))))
-
- (defun prompt-complete-buffer (word)
- (delete-if-not #'(lambda (b)
- (string-head-eq b word))
- (mapcar 'buffer-name buffer-list)))
-
- (defun prompt-validate-buffer (name)
- (if (equal name "")
- t
- (get-buffer name)))
-
- (defvar prompt-file-exclude '"\\.(o|jlc|x)$|~$|^#.*#$"
- "A regexp, if it matches the file being considered for completion, the file
- is rejected.")
-
- ;; Don't want .info files (WB icons) on Amigas, everywhere else they're okay
- ;; though.
- (when (amiga-p)
- (setq prompt-file-exclude (concat prompt-file-exclude "|\\.info$")))
-
- ;; Ignore the `.' and `..' directory entries in UNIX
- (when (unix-p)
- (setq prompt-file-exclude (concat prompt-file-exclude "|^\\.(\\.|)$")))
-
- (defun prompt-complete-filename (word)
- (setq word (expand-file-name word))
- (let*
- ((path (file-name-directory word))
- (file (file-name-nondirectory word))
- (files (directory-files path)))
- (mapcar #'(lambda (x &aux y)
- (when (file-directory-p (setq y (concat path x)))
- (setq y (concat y ?/)))
- y)
- (delete-if #'(lambda (f)
- (or (not (string-head-eq f file))
- (regexp-match prompt-file-exclude f)))
- files))))
-
- (defun prompt-validate-filename (name)
- (file-exists-p name))
-
- (defun prompt-complete-directory (word)
- (setq word (expand-file-name word))
- (let
- ((path (file-name-directory word))
- (file (file-name-nondirectory word)))
- (delq 'nil
- (mapcar #'(lambda (x)
- (when (file-directory-p (concat path x))
- (concat path x ?/)))
- (delete-if #'(lambda (f)
- (not (string-head-eq f file)))
- (directory-files path))))))
-
- (defun prompt-validate-directory (name)
- (file-directory-p name))
-
- (defun prompt-complete-from-list (word)
- (let
- ((src prompt-list)
- (dst ()))
- (while src
- (when (string-head-eq (car src) word)
- (setq dst (cons (car src) dst)))
- (setq src (cdr src)))
- dst))
-
- (defun prompt-validate-from-list (name)
- (when (member name prompt-list)
- t))
-
-
- ;; High-level entrypoints; prompt for a specific type of object
-
- (defun prompt-for-file (&optional prompt existing start)
- "Prompt for a file, if EXISTING is t only files which exist are
- allowed to be entered."
- (unless (stringp prompt)
- (setq prompt "Enter filename:"))
- (unless (stringp start)
- (setq start (file-name-directory (buffer-file-name))))
- (if (and (amiga-p) amiga-use-file-req-p)
- (if existing
- (let
- (file)
- (while (null file)
- (unless (setq file (file-req prompt start))
- (return))
- (unless (file-exists-p file)
- (beep)
- (req "That file doesn't exist!" "Continue")
- (setq file nil)))
- file)
- (file-req prompt start))
- (let*
- ((prompt-completion-function 'prompt-complete-filename)
- (prompt-validate-function (if existing
- 'prompt-validate-filename
- nil))
- (prompt-word-regexps prompt-def-regexps)
- (str (prompt2 prompt start)))
- (when str
- (expand-file-name str)))))
-
- (defun prompt-for-directory (&optional prompt existing start)
- "Prompt for a directory, if EXISTING is t only files which exist are
- allowed to be entered."
- (unless (stringp prompt)
- (setq prompt "Enter filename:"))
- (unless (stringp start)
- (setq start (file-name-directory (buffer-file-name))))
- (let*
- ((prompt-completion-function 'prompt-complete-directory)
- (prompt-validate-function (if existing
- 'prompt-validate-directory
- nil))
- (prompt-word-regexps prompt-def-regexps)
- (str (prompt2 prompt start)))
- (when str
- (expand-file-name str))))
-
- (defun prompt-for-buffer (&optional prompt existing default)
- "Prompt for a buffer, if EXISTING is t the buffer selected must exist,
- otherwise if EXISTING is nil the buffer will be created if it doesn't
- exist already. DEFAULT is the value to return if the user enters the null
- string, if nil the current buffer is returned."
- (unless (stringp prompt)
- (setq prompt "Enter buffer name:"))
- (let*
- ((prompt-completion-function 'prompt-complete-buffer)
- (prompt-validate-function (if existing
- 'prompt-validate-buffer
- nil))
- (prompt-word-regexps prompt-def-regexps)
- (buf (prompt2 prompt)))
- (if (equal buf "")
- (or default (current-buffer))
- (unless (get-buffer buf)
- (when (not existing)
- (open-buffer buf))))))
-
- ;; borrowed from lisp-mode.jl
- (defvar symbol-word-regexps ["[^][()?'\"#; ]" "[][()?'\"#; ]|$"])
-
- (defun prompt-for-symbol (&optional prompt prompt-symbol-predicate)
- "Prompt for an existing symbol. If PROMPT-SYMBOL-PREDICATE is given the
- symbol must agree with it."
- (unless (stringp prompt)
- (setq prompt "Enter name of symbol:"))
- (let
- ((prompt-completion-function 'prompt-complete-symbol)
- (prompt-validate-function 'prompt-validate-symbol)
- (prompt-word-regexps symbol-word-regexps))
- (intern (prompt2 prompt))))
-
- (defun prompt-for-lisp (&optional prompt)
- "Prompt for a lisp object."
- (unless (stringp prompt)
- (setq prompt "Enter a Lisp object:"))
- (let
- ((prompt-completion-function 'prompt-complete-symbol)
- (prompt-validate-function nil)
- (prompt-word-regexps symbol-word-regexps)
- (prompt-symbol-predicate nil))
- (read-from-string (prompt2 prompt))))
-
- (defun prompt-for-function (&optional prompt)
- "Prompt for a function."
- (prompt-for-symbol (or prompt "Enter name of function:")
- 'fboundp))
-
- (defun prompt-for-variable (&optional prompt)
- "Prompt for a variable."
- (prompt-for-symbol (or prompt "Enter name of variable:")
- 'boundp))
-
- (defun prompt-for-command (&optional prompt)
- "Prompt for a command."
- (prompt-for-symbol (or prompt "Enter name of command:")
- 'commandp))
-
- (defun prompt-from-list (prompt-list prompt &optional start)
- "Return a selected choice from the list of options (strings) PROMPT-LIST.
- PROMPT is the title displayed, START the starting choice."
- (let
- ((prompt-completion-function 'prompt-complete-from-list)
- (prompt-validate-function 'prompt-validate-from-list)
- (prompt-word-regexps prompt-def-regexps))
- (prompt2 prompt start)))
-
- (defun prompt-for-string (&optional prompt start)
- (prompt (or prompt "Enter string: " start)))
-
- (defun prompt-for-number (&optional prompt)
- (let
- (num)
- (while (not (numberp num))
- (setq num (read-from-string (prompt (or prompt "Enter number: ")))))
- num))
-