Initial revision
This commit is contained in:
parent
7ce486180b
commit
813f532d2f
6 changed files with 4599 additions and 0 deletions
3076
lisp/ediff.el
Normal file
3076
lisp/ediff.el
Normal file
File diff suppressed because it is too large
Load diff
366
lisp/ielm.el
Normal file
366
lisp/ielm.el
Normal file
|
@ -0,0 +1,366 @@
|
|||
;;; ielm.el --- interaction mode for Emacs Lisp
|
||||
;; Copyright (C) 1994 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: David Smith <maa036@lancaster.ac.uk>
|
||||
;; Created: 25 Feb 1994
|
||||
;; Keywords: lisp
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs; see the file COPYING. If not, write to
|
||||
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Provides a nice interface to evaluating Emacs-Lisp expressions.
|
||||
;; Input is handled by the comint package, and output is passed
|
||||
;; through the pretty-printer.
|
||||
|
||||
;; To install: copy this file to a directory in your load-path, and
|
||||
;; add the line
|
||||
;;
|
||||
;; (autoload 'ielm "ielm" "Start an inferior emacs-lisp session" t)
|
||||
;;
|
||||
;; For completion to work, the comint.el from FSF Emacs 19.23 is
|
||||
;; required. If you do not have it, or if you are running Lemacs,
|
||||
;; also add the following code to your .emacs:
|
||||
;;
|
||||
;; (setq ielm-mode-hook
|
||||
;; '(lambda nil
|
||||
;; (define-key ielm-map "\t"
|
||||
;; '(lambda nil (interactive) (or (ielm-tab)
|
||||
;; (lisp-complete-symbol))))))
|
||||
|
||||
;; To start: M-x ielm. Type C-h m in the *ielm* buffer for more info.
|
||||
|
||||
;; The latest version is available by WWW from
|
||||
;; http://mathssun5.lancs.ac.uk:2080/~maa036/elisp/dir.html
|
||||
;; or by anonymous FTP from
|
||||
;; /anonymous@wingra.stat.wisc.edu:pub/src/emacs-lisp/ielm.el.gz
|
||||
;; or from the author: David M. Smith <maa036@lancaster.ac.uk>
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'comint)
|
||||
(require 'pp)
|
||||
|
||||
;;; User variables
|
||||
|
||||
(defvar ielm-noisy t
|
||||
"*If non-nil, beep on error")
|
||||
|
||||
(defvar ielm-prompt "ELISP> ")
|
||||
|
||||
(defvar ielm-dynamic-return t
|
||||
"*If non-nil, RET either evaluates input or inserts a newline,
|
||||
depending on context")
|
||||
|
||||
(defvar ielm-mode-hook nil
|
||||
"*Hooks to be run when the inferior-emacs-lisp-mode is started")
|
||||
|
||||
;;; System variables
|
||||
|
||||
(defvar ielm-working-buffer nil
|
||||
"Buffer, if any, to use in ielm. Usually buffer-local")
|
||||
|
||||
(defvar ielm-header
|
||||
(concat
|
||||
"*** Welcome to IELM mode version "
|
||||
(substring "$Revision: 1.15 $" 11 -2)
|
||||
" *** Type (describe-mode) for help.\n"
|
||||
"IELM has ABSOLUTELY NO WARRANTY; type (describe-no-warranty) for details\n"))
|
||||
|
||||
(defvar ielm-map nil)
|
||||
(if ielm-map nil
|
||||
(if (string-match "Lucid" emacs-version)
|
||||
;; Lemacs
|
||||
(progn
|
||||
(setq ielm-map (make-sparse-keymap))
|
||||
(set-keymap-parent ielm-map comint-mode-map))
|
||||
;; FSF
|
||||
(setq ielm-map (cons 'keymap comint-mode-map)))
|
||||
(define-key ielm-map "\t" 'comint-dynamic-complete)
|
||||
(define-key ielm-map "\C-m" 'ielm-return)
|
||||
(define-key ielm-map "\C-j" 'ielm-send-input))
|
||||
|
||||
;;; Completion stuff
|
||||
|
||||
(defun ielm-tab nil
|
||||
"Possibly indent the current line as lisp code"
|
||||
(interactive)
|
||||
(if (or (eq (preceding-char) ?\n)
|
||||
(eq (char-syntax (preceding-char)) ? ))
|
||||
(progn
|
||||
(ielm-indent-line)
|
||||
t)))
|
||||
|
||||
(defun ielm-complete-symbol nil
|
||||
"Just like lisp-complete-symbol"
|
||||
;; except that it returns non-nil if completion has occurred
|
||||
(let* ((btick (buffer-modified-tick))
|
||||
(cbuffer (get-buffer " *Completions*"))
|
||||
(ctick (and cbuffer (buffer-modified-tick cbuffer))))
|
||||
(lisp-complete-symbol)
|
||||
;; completion has occurred if:
|
||||
(or
|
||||
;; the buffer has been modified
|
||||
(not (= btick (buffer-modified-tick)))
|
||||
;; a completions buffer has been modifed or created
|
||||
(if cbuffer
|
||||
(not (= ctick (buffer-modified-tick cbuffer)))
|
||||
(get-buffer " *Completions*")))))
|
||||
|
||||
(defun ielm-complete-filename nil
|
||||
;; Completes filenames if in a string
|
||||
(if (nth 3 (parse-partial-sexp comint-last-input-start (point)))
|
||||
(comint-dynamic-complete-filename)))
|
||||
|
||||
(defun ielm-indent-line nil
|
||||
"Indent the current line as lisp code if it is not a prompt line"
|
||||
(if (save-excursion
|
||||
(beginning-of-line)
|
||||
(looking-at comint-prompt-regexp)) nil
|
||||
(lisp-indent-line)))
|
||||
|
||||
;;; Other bindings
|
||||
|
||||
(defun ielm-return nil
|
||||
"Evaluate the sexp at the prompt if it is complete, otherwise newline
|
||||
and indent. If ielm-dynamic-return is nil, just insert a newline."
|
||||
(interactive)
|
||||
(if ielm-dynamic-return
|
||||
(let ((state
|
||||
(save-excursion
|
||||
(end-of-line)
|
||||
(parse-partial-sexp (ielm-pm)
|
||||
(point)))))
|
||||
(if (and (< (car state) 1) (not (nth 3 state)))
|
||||
(ielm-send-input)
|
||||
(newline-and-indent)))
|
||||
(newline)))
|
||||
|
||||
(defun ielm-input-sender (proc input)
|
||||
(setq ielm-input input))
|
||||
|
||||
(defun ielm-send-input nil
|
||||
"Evaluate the Emacs Lisp expression after the prompt"
|
||||
(interactive)
|
||||
(let ((buf (current-buffer))
|
||||
ielm-input) ; set by ielm-input-sender
|
||||
(comint-send-input) ; update history, markers etc.
|
||||
(ielm-eval-input ielm-input)))
|
||||
|
||||
;;; Utility functions
|
||||
|
||||
(defun ielm-is-whitespace (string)
|
||||
"Return non-nil if STRING is all whitespace"
|
||||
(or (string= string "") (string-match "\\`[ \t\n]+\\'" string)))
|
||||
|
||||
(defun ielm-format-errors (errlist)
|
||||
(let ((result ""))
|
||||
(while errlist
|
||||
(setq result (concat result (prin1-to-string (car errlist)) ", "))
|
||||
(setq errlist (cdr errlist)))
|
||||
(substring result 0 -2)))
|
||||
|
||||
|
||||
(defun ielm-format-error (err)
|
||||
"Return a string form of the error ERR"
|
||||
(format "%s%s"
|
||||
(or (get (car err) 'error-message) "Peculiar error")
|
||||
(if (cdr err)
|
||||
(format ": %s" (ielm-format-errors (cdr err)))
|
||||
"")))
|
||||
|
||||
;;; Evaluation
|
||||
|
||||
(defun ielm-eval-input (string)
|
||||
"Evaluate the lisp expression STRING, and pretty-print the result"
|
||||
;; This is the function that actually `sends' the input to the
|
||||
;; `inferior lisp process'. All comint-send-input does is works out
|
||||
;; what that input is. What this function does is evaluates that
|
||||
;; input and produces `output' which gets inserted into the buffer,
|
||||
;; along with a new prompt. A better way of doing this might have
|
||||
;; been to actually send the output to the `cat' process, and write
|
||||
;; this as in output filter that converted sexps in the output
|
||||
;; stream to their evaluated value. But that would have involved
|
||||
;; more process coordination than I was happy to deal with.
|
||||
(let (form ; form to evaluate
|
||||
pos ; End posn of parse in string
|
||||
result ; Result, or error message
|
||||
error-type ; string, nil if no error
|
||||
(output "") ; result to display
|
||||
(wbuf ielm-working-buffer) ; current buffer after evaluation
|
||||
(pmark (ielm-pm)))
|
||||
(if (not (ielm-is-whitespace string))
|
||||
(progn
|
||||
(condition-case err
|
||||
(let (rout)
|
||||
(setq rout (read-from-string string))
|
||||
(setq form (car rout))
|
||||
(setq pos (cdr rout)))
|
||||
(error (setq result (ielm-format-error err))
|
||||
(setq error-type "Read error")))
|
||||
(if error-type nil
|
||||
(if (ielm-is-whitespace (substring string pos))
|
||||
;; need this awful let convolution to work around
|
||||
;; an Emacs bug involving local vbls and let binding
|
||||
(let ((:save :)
|
||||
(::save ::)
|
||||
(:::save :::))
|
||||
(save-excursion
|
||||
(set-buffer ielm-working-buffer)
|
||||
(condition-case err
|
||||
(let ((: :save)
|
||||
(:: ::save)
|
||||
(::: :::save))
|
||||
(save-excursion
|
||||
(setq result (eval form))
|
||||
(setq wbuf (current-buffer))))
|
||||
(error (setq result (ielm-format-error err))
|
||||
(setq error-type "Eval error"))
|
||||
(quit (setq result "Quit during evaluation")
|
||||
(setq error-type "Eval error")))))
|
||||
(setq error-type "IELM error")
|
||||
(setq result "More than one sexp in input")))
|
||||
|
||||
;; If the eval changed the current buffer, mention it here
|
||||
(if (eq wbuf ielm-working-buffer) nil
|
||||
(message "current buffer is now: %s" wbuf)
|
||||
(setq ielm-working-buffer wbuf))
|
||||
|
||||
(goto-char pmark)
|
||||
(if (not error-type)
|
||||
(condition-case err
|
||||
;; Self-referential objects cause loops in the printer, so
|
||||
;; trap quits here. May as well do errors, too
|
||||
(setq output (concat output (pp-to-string result)))
|
||||
(error (setq error-type "IELM Error")
|
||||
(setq result "Error during pretty-printing (bug in pp)"))
|
||||
(quit (setq error-type "IELM Error")
|
||||
(setq result "Quit during pretty-printing"))))
|
||||
(if error-type
|
||||
(progn
|
||||
(if ielm-noisy (ding))
|
||||
(setq output (concat output "*** " error-type " *** "))
|
||||
(setq output (concat output result)))
|
||||
;; There was no error, so shift the ::: values
|
||||
(setq ::: ::)
|
||||
(setq :: :)
|
||||
(setq : result))
|
||||
(setq output (concat output "\n"))))
|
||||
(setq output (concat output ielm-prompt))
|
||||
(comint-output-filter (ielm-process) output)))
|
||||
|
||||
;;; Process and marker utilities
|
||||
|
||||
(defun ielm-process nil
|
||||
"Return the current buffer's process"
|
||||
(get-buffer-process (current-buffer)))
|
||||
|
||||
(defun ielm-pm nil
|
||||
"Return the process mark of the current buffer"
|
||||
(process-mark (get-buffer-process (current-buffer))))
|
||||
|
||||
(defun ielm-set-pm (pos)
|
||||
"Set the process mark in the current buffer to POS"
|
||||
(set-marker (process-mark (get-buffer-process (current-buffer))) pos))
|
||||
|
||||
;;; Major mode
|
||||
|
||||
(defun inferior-emacs-lisp-mode nil
|
||||
"Major mode for interactively evaluating Emacs-Lisp expressions
|
||||
Uses the interface provided by `comint-mode' (q.v.)
|
||||
|
||||
\\[ielm-send-input] evaluates the sexp following the prompt. There must be at most
|
||||
one top-level sexp per prompt.
|
||||
\\[ielm-return] inserts a newline and indents. However, if the variable
|
||||
ielm-dynamic-return is non-nil (the default) then it will also evaluate
|
||||
a complete expression.
|
||||
\\[comint-dynamic-complete] completes lisp symbols (or filenames, within strings),
|
||||
or indents the line if there is nothing to complete.
|
||||
|
||||
During evaluations, the values of the variables `:', `::', and `:::'
|
||||
are the results of the previous, second previous and third previous
|
||||
evaluations respectively.
|
||||
|
||||
The current buffer may be changed, and its value is preserved between
|
||||
successive evaluations. In this way, expressions may be evaluated in
|
||||
a different buffer than the *ielm* buffer.
|
||||
|
||||
Expressions evaluated by IELM are not subject to debug-on-quit or
|
||||
debug-on-error.
|
||||
|
||||
The behaviour of IELM may be customised with the following variables:
|
||||
* To stop beeping on error, set `ielm-noisy' to nil
|
||||
* If you don't like the prompt, you can change it by setting `ielm-prompt'.
|
||||
* Set `ielm-dynamic-return' to nil for bindings like `lisp-interaction-mode'
|
||||
* Entry to this mode runs `comint-mode-hook' and `ielm-mode-hook'
|
||||
(in that order).
|
||||
|
||||
Customised bindings may be defined in `ielm-map', which currently contains:
|
||||
\\{ielm-map}"
|
||||
(interactive)
|
||||
(comint-mode)
|
||||
(setq comint-prompt-regexp (concat "^" (regexp-quote ielm-prompt)))
|
||||
(make-local-variable 'paragraph-start)
|
||||
(setq paragraph-start comint-prompt-regexp)
|
||||
(setq comint-input-sender 'ielm-input-sender)
|
||||
(setq comint-process-echoes nil)
|
||||
(setq comint-dynamic-complete-functions
|
||||
'(ielm-tab comint-replace-by-expanded-history ielm-complete-filename ielm-complete-symbol))
|
||||
|
||||
(setq major-mode 'inferior-emacs-lisp-mode)
|
||||
(setq mode-name "IELM")
|
||||
(use-local-map ielm-map)
|
||||
(set-syntax-table emacs-lisp-mode-syntax-table)
|
||||
|
||||
(make-local-variable 'indent-line-function)
|
||||
(make-local-variable 'ielm-working-buffer)
|
||||
(setq ielm-working-buffer (current-buffer))
|
||||
(setq indent-line-function 'ielm-indent-line)
|
||||
|
||||
;;; Value holders
|
||||
(setq : nil)
|
||||
(make-local-variable ':)
|
||||
(setq :: nil)
|
||||
(make-local-variable '::)
|
||||
(setq ::: nil)
|
||||
(make-local-variable ':::)
|
||||
|
||||
;; A dummy process to keep comint happy. It will never get any input
|
||||
(if (comint-check-proc (current-buffer)) nil
|
||||
(start-process "ielm" (current-buffer) "cat")
|
||||
(process-kill-without-query (ielm-process))
|
||||
(goto-char (point-max))
|
||||
;; Add a silly header
|
||||
(insert ielm-header)
|
||||
(ielm-set-pm (point-max))
|
||||
(comint-output-filter (ielm-process) ielm-prompt)
|
||||
(set-marker comint-last-input-start (ielm-pm))
|
||||
(set-process-filter (get-buffer-process (current-buffer)) 'comint-output-filter))
|
||||
(run-hooks 'ielm-mode-hook))
|
||||
|
||||
;;; User command
|
||||
|
||||
(defun ielm nil
|
||||
"Switch to or create the buffer *ielm* for evaluating emacs-lisp expressions"
|
||||
(interactive)
|
||||
(if (comint-check-proc "*ielm*") nil
|
||||
(progn
|
||||
(set-buffer (get-buffer-create "*ielm*"))
|
||||
(inferior-emacs-lisp-mode)))
|
||||
(switch-to-buffer "*ielm*"))
|
||||
|
||||
;; ielm.el ends here
|
282
lisp/mail/mail-hist.el
Normal file
282
lisp/mail/mail-hist.el
Normal file
|
@ -0,0 +1,282 @@
|
|||
;;; mail-hist.el --- Headers and message body history for outgoing mail.
|
||||
;; Copyright (C) 1994 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Karl Fogel <kfogel@cs.oberlin.edu>
|
||||
;; Created: March, 1994
|
||||
;; Version: 1.2.2
|
||||
;; Keywords: mail
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to
|
||||
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
;; Thanks to Jim Blandy for mentioning ring.el. It saved a lot of
|
||||
;; time.
|
||||
;;
|
||||
;; To use this package, put it in a directory in your load-path, and
|
||||
;; put this in your .emacs file:
|
||||
;;
|
||||
;; (load "mail-hist" nil t)
|
||||
;;
|
||||
;; Or you could do it with autoloads and hooks in your .emacs:
|
||||
;;
|
||||
;; (add-hook 'mail-mode-hook 'mail-hist-define-keys)
|
||||
;; (add-hook 'mail-send-hook 'mail-hist-put-headers-into-history)
|
||||
;; (add-hook 'vm-mail-mode-hook 'mail-hist-define-keys) ;or rmail, etc
|
||||
;; (autoload 'mail-hist-define-keys "mail-hist")
|
||||
;; (autoload 'mail-hist-put-headers-into-history "mail-hist")
|
||||
;;
|
||||
;; Once it's installed, use M-p and M-n from mail headers to recover
|
||||
;; previous/next contents in the history for that header, or, in the
|
||||
;; body of the message, to recover previous/next text of the message.
|
||||
;; This only applies to outgoing mail -- mail-hist ignores received
|
||||
;; messages.
|
||||
;;
|
||||
;; Although repeated history requests do clear out the text from the
|
||||
;; previous request, an isolated request just inserts its text at
|
||||
;; point, so that you can mix the histories of different messages
|
||||
;; easily. This might be confusing at times, but there should be no
|
||||
;; problems that undo can't handle.
|
||||
|
||||
;;; Code:
|
||||
(require 'ring)
|
||||
|
||||
;;;###autoload
|
||||
(defun mail-hist-define-keys ()
|
||||
"Define keys for accessing mail header history. For use in hooks."
|
||||
(local-set-key "\M-p" 'mail-hist-previous-input)
|
||||
(local-set-key "\M-n" 'mail-hist-next-input))
|
||||
|
||||
;;;###autoload
|
||||
(add-hook 'mail-mode-hook 'mail-hist-define-keys)
|
||||
|
||||
;;;###autoload
|
||||
(add-hook 'vm-mail-mode-hook 'mail-hist-define-keys)
|
||||
|
||||
;;;###autoload
|
||||
(add-hook 'mail-send-hook 'mail-hist-put-headers-into-history)
|
||||
|
||||
(defvar mail-hist-header-ring-alist nil
|
||||
"Alist of form (header-name . history-ring).
|
||||
Used for knowing which history list to look in when the user asks for
|
||||
previous/next input.")
|
||||
|
||||
(defvar mail-hist-history-size (or kill-ring-max 1729)
|
||||
"*The maximum number of elements in a mail field's history.
|
||||
Oldest elements are dumped first.")
|
||||
|
||||
;;;###autoload
|
||||
(defvar mail-hist-keep-history t
|
||||
"*Non-nil means keep a history for headers and text of outgoing mail.")
|
||||
|
||||
;; For handling repeated history requests
|
||||
(defvar mail-hist-access-count 0)
|
||||
|
||||
(defvar mail-hist-last-bounds nil)
|
||||
;; (start . end) A pair indicating the buffer positions delimiting the
|
||||
;; last inserted history, so it can be replaced by a new input if the
|
||||
;; command is repeated.
|
||||
|
||||
(defvar mail-hist-header-regexp "^[^:]*:"
|
||||
"Regular expression for matching headers in a mail message.")
|
||||
|
||||
(defsubst mail-hist-current-header-name ()
|
||||
"Get name of mail header point is currently in, without the colon.
|
||||
Returns nil if not in a header, implying that point is in the body of
|
||||
the message."
|
||||
(if (save-excursion
|
||||
(re-search-backward (concat "^" mail-header-separator) nil t))
|
||||
nil ; then we are in the body of the message
|
||||
(save-excursion
|
||||
(let* ((body-start ; limit possibility of false headers
|
||||
(save-excursion
|
||||
(re-search-forward (concat "^" mail-header-separator) nil t)))
|
||||
(name-start
|
||||
(re-search-backward mail-hist-header-regexp nil t))
|
||||
(name-end
|
||||
(prog2 (search-forward ":" body-start t) (1- (point)))))
|
||||
(and
|
||||
name-start
|
||||
name-end
|
||||
(buffer-substring name-start name-end))))))
|
||||
|
||||
(defsubst mail-hist-forward-header (count)
|
||||
"Move forward COUNT headers (backward if COUNT is negative).
|
||||
If last/first header is encountered first, stop there and returns
|
||||
nil.
|
||||
|
||||
Places point on the first non-whitespace on the line following the
|
||||
colon after the header name, or on the second space following that if
|
||||
the header is empty."
|
||||
(let ((boundary (save-excursion
|
||||
(re-search-forward (concat "^" mail-header-separator) nil t))))
|
||||
(and
|
||||
boundary
|
||||
(let ((unstopped t))
|
||||
(setq boundary (save-excursion
|
||||
(goto-char boundary)
|
||||
(beginning-of-line)
|
||||
(1- (point))))
|
||||
(if (> count 0)
|
||||
(while (> count 0)
|
||||
(setq
|
||||
unstopped
|
||||
(re-search-forward mail-hist-header-regexp boundary t))
|
||||
(setq count (1- count)))
|
||||
;; because the current header will match too.
|
||||
(setq count (1- count))
|
||||
;; count is negative
|
||||
(while (< count 0)
|
||||
(setq
|
||||
unstopped
|
||||
(re-search-backward mail-hist-header-regexp nil t))
|
||||
(setq count (1+ count)))
|
||||
;; we end up behind the header, so must move to the front
|
||||
(re-search-forward mail-hist-header-regexp boundary t))
|
||||
;; Now we are right after the colon
|
||||
(and (looking-at "\\s-") (forward-char 1))
|
||||
;; return nil if didn't go as far as asked, otherwise point
|
||||
unstopped))))
|
||||
|
||||
(defsubst mail-hist-beginning-of-header ()
|
||||
"Move to the start of the current header.
|
||||
The start of the current header is defined as one space after the
|
||||
colon, or just after the colon if it is not followed by whitespace."
|
||||
;; this is slick as all heck:
|
||||
(if (mail-hist-forward-header -1)
|
||||
(mail-hist-forward-header 1)
|
||||
(mail-hist-forward-header 1)
|
||||
(mail-hist-forward-header -1)))
|
||||
|
||||
(defsubst mail-hist-current-header-contents ()
|
||||
"Get the contents of the mail header in which point is located."
|
||||
(save-excursion
|
||||
(mail-hist-beginning-of-header)
|
||||
(let ((start (point)))
|
||||
(or (mail-hist-forward-header 1)
|
||||
(re-search-forward (concat "^" mail-header-separator)))
|
||||
(beginning-of-line)
|
||||
(buffer-substring start (1- (point))))))
|
||||
|
||||
(defsubst mail-hist-get-header-ring (header)
|
||||
"Get HEADER's history ring, or nil if none.
|
||||
HEADER is a string without the colon."
|
||||
(cdr (assoc header mail-hist-header-ring-alist)))
|
||||
|
||||
(defsubst mail-hist-add-header-contents-to-ring (header &optional contents)
|
||||
"Add the contents of HEADER to the header history ring.
|
||||
Optional argument CONTENTS is a string which will be the contents
|
||||
(instead of whatever's found in the header)."
|
||||
(let ((ring (cdr (assoc header mail-hist-header-ring-alist))))
|
||||
(or ring
|
||||
;; If the ring doesn't exist, we'll have to make it and add it
|
||||
;; to the mail-header-ring-alist:
|
||||
(prog1
|
||||
(setq ring (make-ring mail-hist-history-size))
|
||||
(setq mail-hist-header-ring-alist
|
||||
(cons (cons header ring) mail-hist-header-ring-alist))))
|
||||
(ring-insert
|
||||
ring
|
||||
(or contents (mail-hist-current-header-contents)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun mail-hist-put-headers-into-history ()
|
||||
"Put headers and contents of this message into mail header history.
|
||||
Each header has its own independent history, as does the body of the
|
||||
message.
|
||||
|
||||
This function normally would be called when the message is sent."
|
||||
(and
|
||||
mail-hist-keep-history
|
||||
(progn
|
||||
(goto-char (point-min))
|
||||
(while (mail-hist-forward-header 1)
|
||||
(mail-hist-add-header-contents-to-ring
|
||||
(mail-hist-current-header-name)))
|
||||
(let ((body-contents
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(re-search-forward (concat "^" mail-header-separator) nil)
|
||||
(forward-line 1)
|
||||
(buffer-substring (point) (point-max)))))
|
||||
(mail-hist-add-header-contents-to-ring "body" body-contents)))))
|
||||
|
||||
(defun mail-hist-previous-input (header)
|
||||
"Insert the previous contents of this mail header or message body.
|
||||
Moves back through the history of sent mail messages. Each header has
|
||||
its own independent history, as does the body of the message.
|
||||
|
||||
The history only contains the contents of outgoing messages, not
|
||||
received mail."
|
||||
(interactive (list (or (mail-hist-current-header-name) "body")))
|
||||
(let* ((ring (cdr (assoc header mail-hist-header-ring-alist)))
|
||||
(len (ring-length ring))
|
||||
(repeat (eq last-command 'mail-hist-input-access)))
|
||||
(if repeat
|
||||
(setq mail-hist-access-count
|
||||
(ring-plus1 mail-hist-access-count len))
|
||||
(setq mail-hist-access-count 0))
|
||||
(if (null ring)
|
||||
(progn
|
||||
(ding)
|
||||
(message "No history for \"%s\"." header))
|
||||
(if (ring-empty-p ring)
|
||||
(error "\"%s\" ring is empty." header)
|
||||
(and repeat
|
||||
(delete-region (car mail-hist-last-bounds)
|
||||
(cdr mail-hist-last-bounds)))
|
||||
(let ((start (point)))
|
||||
(insert (ring-ref ring mail-hist-access-count))
|
||||
(setq mail-hist-last-bounds (cons start (point)))
|
||||
(setq this-command 'mail-hist-input-access))))))
|
||||
|
||||
(defun mail-hist-next-input (header)
|
||||
"Insert next contents of this mail header or message body.
|
||||
Moves back through the history of sent mail messages. Each header has
|
||||
its own independent history, as does the body of the message.
|
||||
|
||||
Although you can do so, it does not make much sense to call this
|
||||
without having called `mail-hist-previous-header' first
|
||||
(\\[mail-hist-previous-header]).
|
||||
|
||||
The history only contains the contents of outgoing messages, not
|
||||
received mail."
|
||||
(interactive (list (or (mail-hist-current-header-name) "body")))
|
||||
(let* ((ring (cdr (assoc header mail-hist-header-ring-alist)))
|
||||
(len (ring-length ring))
|
||||
(repeat (eq last-command 'mail-hist-input-access)))
|
||||
(if repeat
|
||||
(setq mail-hist-access-count
|
||||
(ring-minus1 mail-hist-access-count len))
|
||||
(setq mail-hist-access-count 0))
|
||||
(if (null ring)
|
||||
(progn
|
||||
(ding)
|
||||
(message "No history for \"%s\"." header))
|
||||
(if (ring-empty-p ring)
|
||||
(error "\"%s\" ring is empty." header)
|
||||
(and repeat
|
||||
(delete-region (car mail-hist-last-bounds)
|
||||
(cdr mail-hist-last-bounds)))
|
||||
(let ((start (point)))
|
||||
(insert (ring-ref ring mail-hist-access-count))
|
||||
(setq mail-hist-last-bounds (cons start (point)))
|
||||
(setq this-command 'mail-hist-input-access))))))
|
||||
|
||||
(provide 'mail-hist)
|
||||
|
||||
;; mail-hist.el ends here
|
226
lisp/mldrag.el
Normal file
226
lisp/mldrag.el
Normal file
|
@ -0,0 +1,226 @@
|
|||
;;; mldrag.el -- Mode line and vertical line dragging to resize windows.
|
||||
;;; Copyright (C) 1994 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Kyle E. Jones <kyle@wonderworks.com>
|
||||
;; Keywords: mouse
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs; see the file COPYING. If not, write to
|
||||
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This package lets you drag the modeline, vertical bar and
|
||||
;; scrollbar to resize windows. Suggested bindings are:
|
||||
;;
|
||||
;; (global-set-key [mode-line down-mouse-1] 'mldrag-drag-mode-line)
|
||||
;; (global-set-key [vertical-line down-mouse-1] 'mldrag-drag-vertical-line)
|
||||
;; (global-set-key [vertical-scroll-bar S-down-mouse-1]
|
||||
;; 'mldrag-drag-vertical-line)
|
||||
;;
|
||||
;; Put the bindings and (require 'mldrag) in your .emacs file.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(provide 'mldrag)
|
||||
|
||||
(defun mldrag-drag-mode-line (start-event)
|
||||
"Change the height of the current window with the mouse.
|
||||
This command should be bound to a down-mouse- event, and is most
|
||||
usefully bound with the `mode-line' prefix. Holding down a mouse
|
||||
button and moving the mouse up and down will make the clicked-on
|
||||
window taller or shorter."
|
||||
(interactive "e")
|
||||
(let ((done nil)
|
||||
(echo-keystrokes 0)
|
||||
(start-event-frame (window-frame (car (car (cdr start-event)))))
|
||||
(start-event-window (car (car (cdr start-event))))
|
||||
(start-nwindows (count-windows t))
|
||||
(old-selected-window (selected-window))
|
||||
should-enlarge-minibuffer
|
||||
event mouse minibuffer y top bot edges wconfig params growth)
|
||||
(setq params (frame-parameters))
|
||||
(if (and (not (setq minibuffer (cdr (assq 'minibuffer params))))
|
||||
(one-window-p t))
|
||||
(error "Attempt to resize sole window"))
|
||||
(unwind-protect
|
||||
(track-mouse
|
||||
(progn
|
||||
;; enlarge-window only works on the selected window, so
|
||||
;; we must select the window where the start event originated.
|
||||
;; unwind-protect will restore the old selected window later.
|
||||
(select-window start-event-window)
|
||||
;; if this is the bottommost ordinary window, then to
|
||||
;; move its modeline the minibuffer must be enlarged.
|
||||
(setq should-enlarge-minibuffer
|
||||
(and minibuffer
|
||||
(not (one-window-p t))
|
||||
(= (nth 1 (window-edges minibuffer))
|
||||
(nth 3 (window-edges)))))
|
||||
;; loop reading events and sampling the position of
|
||||
;; the mouse.
|
||||
(while (not done)
|
||||
(setq event (read-event)
|
||||
mouse (mouse-position))
|
||||
;; do nothing if
|
||||
;; - there is a switch-frame event.
|
||||
;; - the mouse isn't in the frame that we started in
|
||||
;; - the mouse isn't in any Emacs frame
|
||||
;; drag if
|
||||
;; - there is a mouse-movement event
|
||||
;; - there is a scroll-bar-movement event
|
||||
;; (same as mouse movement for our purposes)
|
||||
;; quit if
|
||||
;; - there is a keyboard event or some other unknown event
|
||||
;; unknown event.
|
||||
(cond ((integerp event)
|
||||
(setq done t))
|
||||
((eq (car event) 'switch-frame)
|
||||
nil)
|
||||
((not (memq (car event)
|
||||
'(mouse-movement scroll-bar-movement)))
|
||||
(setq done t))
|
||||
((not (eq (car mouse) start-event-frame))
|
||||
nil)
|
||||
((null (car (cdr mouse)))
|
||||
nil)
|
||||
(t
|
||||
(setq y (cdr (cdr mouse))
|
||||
edges (window-edges)
|
||||
top (nth 1 edges)
|
||||
bot (nth 3 edges))
|
||||
;; scale back a move that would make the
|
||||
;; window too short.
|
||||
(cond ((< (- y top -1) window-min-height)
|
||||
(setq y (+ top window-min-height -1))))
|
||||
;; compute size change needed
|
||||
(setq growth (- y bot -1)
|
||||
wconfig (current-window-configuration))
|
||||
;; grow/shrink minibuffer?
|
||||
(if should-enlarge-minibuffer
|
||||
(progn
|
||||
;; yes. briefly select minibuffer so
|
||||
;; ealarge-window will affect the
|
||||
;; correct window.
|
||||
(select-window minibuffer)
|
||||
;; scale back shrinkage if it would
|
||||
;; make the minibuffer less than 1
|
||||
;; line tall.
|
||||
(if (and (> growth 0)
|
||||
(< (- (window-height minibuffer)
|
||||
growth)
|
||||
1))
|
||||
(setq growth (1- (window-height minibuffer))))
|
||||
(enlarge-window (- growth))
|
||||
(select-window start-event-window))
|
||||
;; no. grow/shrink the selected window
|
||||
(enlarge-window growth))
|
||||
;; if this window's growth caused another
|
||||
;; window to be deleted because it was too
|
||||
;; short, rescind the change.
|
||||
;;
|
||||
;; if size change caused space to be stolen
|
||||
;; from a window above this one, rescind the
|
||||
;; change, but only if we didn't grow/srhink
|
||||
;; the minibuffer. minibuffer size changes
|
||||
;; can cause all windows to shrink... no way
|
||||
;; around it.
|
||||
(if (or (/= start-nwindows (count-windows t))
|
||||
(and (not should-enlarge-minibuffer)
|
||||
(/= top (nth 1 (window-edges)))))
|
||||
(set-window-configuration wconfig)))))))
|
||||
;; restore the old selected window
|
||||
(select-window old-selected-window))))
|
||||
|
||||
(defun mldrag-drag-vertical-line (start-event)
|
||||
"Change the width of the current window with the mouse.
|
||||
This command should be bound to a down-mouse- event, and is most
|
||||
usefully bound with the `vertical-line' or the `vertical-scroll-bar'
|
||||
prefix. Holding down a mouse button and moving the mouse left and
|
||||
right will make the clicked-on window thinner or wider."
|
||||
(interactive "e")
|
||||
(let ((done nil)
|
||||
(echo-keystrokes 0)
|
||||
(start-event-frame (window-frame (car (car (cdr start-event)))))
|
||||
(start-event-window (car (car (cdr start-event))))
|
||||
(start-nwindows (count-windows t))
|
||||
(old-selected-window (selected-window))
|
||||
event mouse x left right edges wconfig growth)
|
||||
(if (one-window-p t)
|
||||
(error "Attempt to resize sole ordinary window"))
|
||||
(if (= (nth 2 (window-edges start-event-window))
|
||||
(frame-width start-event-frame))
|
||||
(error "Attempt to drag rightmost scrollbar"))
|
||||
(unwind-protect
|
||||
(track-mouse
|
||||
(progn
|
||||
;; enlarge-window only works on the selected window, so
|
||||
;; we must select the window where the start event originated.
|
||||
;; unwind-protect will restore the old selected window later.
|
||||
(select-window start-event-window)
|
||||
;; loop reading events and sampling the position of
|
||||
;; the mouse.
|
||||
(while (not done)
|
||||
(setq event (read-event)
|
||||
mouse (mouse-position))
|
||||
;; do nothing if
|
||||
;; - there is a switch-frame event.
|
||||
;; - the mouse isn't in the frame that we started in
|
||||
;; - the mouse isn't in any Emacs frame
|
||||
;; drag if
|
||||
;; - there is a mouse-movement event
|
||||
;; - there is a scroll-bar-movement event
|
||||
;; (same as mouse movement for our purposes)
|
||||
;; quit if
|
||||
;; - there is a keyboard event or some other unknown event
|
||||
;; unknown event.
|
||||
(cond ((integerp event)
|
||||
(setq done t))
|
||||
((eq (car event) 'switch-frame)
|
||||
nil)
|
||||
((not (memq (car event)
|
||||
'(mouse-movement scroll-bar-movement)))
|
||||
(setq done t))
|
||||
((not (eq (car mouse) start-event-frame))
|
||||
nil)
|
||||
((null (car (cdr mouse)))
|
||||
nil)
|
||||
(t
|
||||
(setq x (car (cdr mouse))
|
||||
edges (window-edges)
|
||||
left (nth 0 edges)
|
||||
right (nth 2 edges))
|
||||
;; scale back a move that would make the
|
||||
;; window too thin.
|
||||
(cond ((< (- x left -1) window-min-width)
|
||||
(setq x (+ left window-min-width -1))))
|
||||
;; compute size change needed
|
||||
(setq growth (- x right -1)
|
||||
wconfig (current-window-configuration))
|
||||
(enlarge-window growth t)
|
||||
;; if this window's growth caused another
|
||||
;; window to be deleted because it was too
|
||||
;; thin, rescind the change.
|
||||
;;
|
||||
;; if size change caused space to be stolen
|
||||
;; from a window to the left of this one,
|
||||
;; rescind the change.
|
||||
(if (or (/= start-nwindows (count-windows t))
|
||||
(/= left (nth 0 (window-edges))))
|
||||
(set-window-configuration wconfig)))))))
|
||||
;; restore the old selected window
|
||||
(select-window old-selected-window))))
|
||||
|
||||
;; mldrag.el ends here
|
200
lisp/rsz-mini.el
Normal file
200
lisp/rsz-mini.el
Normal file
|
@ -0,0 +1,200 @@
|
|||
;;; rsz-mini.el --- dynamically resize minibuffer to display entire contents
|
||||
|
||||
;;; Copyright (C) 1990, 1993, 1994 Free Software Foundation, Inc.
|
||||
|
||||
;;; Author: Noah Friedman <friedman@prep.ai.mit.edu>
|
||||
;;; Roland McGrath <roland@prep.ai.mit.edu>
|
||||
;;; Maintainer: friedman@prep.ai.mit.edu
|
||||
;;; Keywords: minibuffer, window, frame, display
|
||||
;;; Status: Known to work in FSF GNU Emacs 19.23.
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs; see the file COPYING. If not, write to
|
||||
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; This package allows the entire contents (or as much as possible) of the
|
||||
;;; minibuffer to be visible at once when typing. As the end of a line is
|
||||
;;; reached, the minibuffer will resize itself. When the user is done
|
||||
;;; typing, the minibuffer will return to its original size.
|
||||
|
||||
;;; In window systems where it is possible to have a frame in which the
|
||||
;;; minibuffer is the only window, the frame itself can be resized. In FSF
|
||||
;;; GNU Emacs 19.22 and earlier, the frame may not be properly returned to
|
||||
;;; its original size after it ceases to be active because
|
||||
;;; `minibuffer-exit-hook' didn't exist until version 19.23.
|
||||
|
||||
;;; Note that the minibuffer and echo area are not the same! They simply
|
||||
;;; happen to occupy roughly the same place on the frame. Messages put in
|
||||
;;; the echo area will not cause any resizing by this package.
|
||||
|
||||
;;; This package is considered a minor mode but it doesn't put anything in
|
||||
;;; minor-mode-alist because this mode is specific to the minibuffer, which
|
||||
;;; has no mode line.
|
||||
|
||||
;;; To use this package, put the following in your .emacs:
|
||||
;;;
|
||||
;;; (autoload 'resize-minibuffer-mode "rsz-mini" nil t)
|
||||
;;;
|
||||
;;; Invoking the command `resize-minibuffer-mode' will then enable this mode.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
||||
;;;###autoload
|
||||
(defvar resize-minibuffer-mode nil
|
||||
"*If non-`nil', resize the minibuffer so its entire contents are visible.")
|
||||
|
||||
;;;###autoload
|
||||
(defvar resize-minibuffer-window-max-height nil
|
||||
"*Maximum size the minibuffer window is allowed to become.
|
||||
If less than 1 or not a number, the limit is the height of the frame in
|
||||
which the active minibuffer window resides.")
|
||||
|
||||
;;;###autoload
|
||||
(defvar resize-minibuffer-window-exactly t
|
||||
"*If non-`nil', make minibuffer exactly the size needed to display all its contents.
|
||||
Otherwise, the minibuffer window can temporarily increase in size but
|
||||
never get smaller while it is active.")
|
||||
|
||||
|
||||
;;;###autoload
|
||||
(defvar resize-minibuffer-frame nil
|
||||
"*If non-`nil' and the active minibuffer is the sole window in its frame, allow changing the frame height.")
|
||||
|
||||
;;;###autoload
|
||||
(defvar resize-minibuffer-frame-max-height nil
|
||||
"*Maximum size the minibuffer frame is allowed to become.
|
||||
If less than 1 or not a number, there is no limit.")
|
||||
|
||||
;;;###autoload
|
||||
(defvar resize-minibuffer-frame-exactly nil
|
||||
"*If non-`nil', make minibuffer frame exactly the size needed to display all its contents.
|
||||
Otherwise, the minibuffer frame can temporarily increase in size but
|
||||
never get smaller while it is active.")
|
||||
|
||||
|
||||
;;;###autoload
|
||||
(defun resize-minibuffer-mode (&optional prefix)
|
||||
"Enable or disable resize-minibuffer mode.
|
||||
A negative prefix argument disables this mode. A positive argument or
|
||||
argument of 0 enables it.
|
||||
|
||||
When this minor mode is enabled, the minibuffer is dynamically resized to
|
||||
contain the entire region of text put in it as you type.
|
||||
|
||||
The variable `resize-minibuffer-mode' is set to t or nil depending on
|
||||
whether this mode is active or not.
|
||||
|
||||
The maximum height to which the minibuffer can grow is controlled by the
|
||||
variable `resize-minibuffer-window-max-height'.
|
||||
|
||||
The variable `resize-minibuffer-window-exactly' determines whether the
|
||||
minibuffer window should ever be shrunk to make it no larger than needed to
|
||||
display its contents.
|
||||
|
||||
When using a window system, it is possible for a minibuffer to tbe the sole
|
||||
window in a frame. Since that window is already its maximum size, the only
|
||||
way to make more text visible at once is to increase the size of the frame.
|
||||
The variable `resize-minibuffer-frame' controls whether this should be
|
||||
done. The variables `resize-minibuffer-frame-max-height' and
|
||||
`resize-minibuffer-frame-exactly' are analogous to their window
|
||||
counterparts."
|
||||
(interactive "p")
|
||||
(or prefix (setq prefix 0))
|
||||
(cond
|
||||
((>= prefix 0)
|
||||
(setq resize-minibuffer-mode t))
|
||||
(t
|
||||
(setq resize-minibuffer-mode nil))))
|
||||
|
||||
(defun resize-minibuffer-setup ()
|
||||
(cond
|
||||
(resize-minibuffer-mode
|
||||
(cond
|
||||
((and window-system
|
||||
(eq 'only (cdr (assq 'minibuffer (frame-parameters)))))
|
||||
(and resize-minibuffer-frame
|
||||
(progn
|
||||
(make-local-variable 'minibuffer-exit-hook)
|
||||
(add-hook 'minibuffer-exit-hook 'resize-minibuffer-frame-restore)
|
||||
(make-local-variable 'post-command-hook)
|
||||
(add-hook 'post-command-hook 'resize-minibuffer-frame))))
|
||||
(t
|
||||
(make-local-variable 'post-command-hook)
|
||||
(add-hook 'post-command-hook 'resize-minibuffer-window))))))
|
||||
|
||||
(defun resize-minibuffer-count-window-lines (&optional start end)
|
||||
"Return number of window lines occupied by text in region.
|
||||
The number of window lines may be greater than the number of actual lines
|
||||
in the buffer if any wrap on the display due to their length.
|
||||
|
||||
Optional arguments START and END default to point-min and point-max,
|
||||
respectively."
|
||||
(or start (setq start (point-min)))
|
||||
(or end (setq end (point-max)))
|
||||
(if (= start end)
|
||||
0
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(widen)
|
||||
(narrow-to-region start end)
|
||||
(goto-char start)
|
||||
(vertical-motion (buffer-size))))))
|
||||
|
||||
|
||||
;; Resize the minibuffer window to contain the minibuffer's contents.
|
||||
;; The minibuffer must be the current window.
|
||||
(defun resize-minibuffer-window ()
|
||||
(let ((height (window-height))
|
||||
(lines (1+ (resize-minibuffer-count-window-lines))))
|
||||
(and (numberp resize-minibuffer-window-max-height)
|
||||
(> resize-minibuffer-window-max-height 0)
|
||||
(setq lines (min lines resize-minibuffer-window-max-height)))
|
||||
(or (if resize-minibuffer-window-exactly
|
||||
(= lines height)
|
||||
(<= lines height))
|
||||
(enlarge-window (- lines height)))))
|
||||
|
||||
|
||||
;; Resize the minibuffer frame to contain the minibuffer's contents.
|
||||
;; The minibuffer frame must be the current frame.
|
||||
(defun resize-minibuffer-frame ()
|
||||
(let ((height (frame-height))
|
||||
(lines (1+ (resize-minibuffer-count-window-lines))))
|
||||
(and (numberp resize-minibuffer-frame-max-height)
|
||||
(> resize-minibuffer-frame-max-height 0)
|
||||
(setq lines (min lines resize-minibuffer-frame-max-height)))
|
||||
(cond
|
||||
((> lines height)
|
||||
(set-frame-size (selected-frame) (frame-width) lines))
|
||||
((and resize-minibuffer-frame-exactly
|
||||
(> height (cdr (assq 'height minibuffer-frame-alist)))
|
||||
(< lines height))
|
||||
(set-frame-size (selected-frame) (frame-width) lines)))))
|
||||
|
||||
;; Restore the original height of the frame.
|
||||
(defun resize-minibuffer-frame-restore ()
|
||||
(set-frame-size (selected-frame)
|
||||
(frame-width)
|
||||
(cdr (assq 'height minibuffer-frame-alist))))
|
||||
|
||||
|
||||
(provide 'rsz-mini)
|
||||
|
||||
(add-hook 'minibuffer-setup-hook 'resize-minibuffer-setup)
|
||||
|
||||
;; rsz-mini.el ends here
|
449
lisp/tempo.el
Normal file
449
lisp/tempo.el
Normal file
|
@ -0,0 +1,449 @@
|
|||
;;; tempo.el --- templates with hotspots
|
||||
;; Copyright (C) 1994 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: David Kegedal <davidk@lysator.liu.se >
|
||||
;; Created: 16 Feb 1994
|
||||
;; Version: 1.0
|
||||
;; Keywords: extensions, languages, tools
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs; see the file COPYING. If not, write to
|
||||
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This file provides a simple way to define powerful templates, or
|
||||
;; macros, if you wish. It is mainly intended for, but not limited to,
|
||||
;; other programmers to be used for creating shortcuts for editing
|
||||
;; certain kind of documents. It was originally written to be used by
|
||||
;; a HTML editing mode written by Nelson Minar <nelson@reed.edu>, and
|
||||
;; his html-helper-mode.el is probably the best example of how to use
|
||||
;; this program.
|
||||
|
||||
;; A template is defined as a list of items to be inserted in the
|
||||
;; current buffer at point. Some of the items can be simple strings,
|
||||
;; while other can control formatting or define special points of
|
||||
;; interest in the inserted text.
|
||||
|
||||
;; If a template defines a "point of interest" that point is inserted
|
||||
;; in a buffer-local list of "points of interest" that the user can
|
||||
;; jump between with the commands `tempo-backward-mark' and
|
||||
;; `tempo-forward-mark'. If the template definer provides a prompt for
|
||||
;; the point, and the variable `tempo-interactive' is non-nil, the
|
||||
;; user will be prompted for a string to be inserted in the buffer,
|
||||
;; using the minibuffer.
|
||||
|
||||
;; The template can also define one point to be replaced with the
|
||||
;; current region if the template command is called with a prefix (or
|
||||
;; a non-nil argument).
|
||||
|
||||
;; More flexible templates can be created by including lisp symbols,
|
||||
;; which will be evaluated as variables, or lists, which will will be
|
||||
;; evaluated as lisp expressions.
|
||||
|
||||
;; See the documentation for tempo-define-template for the different
|
||||
;; items that can be used to define a tempo template.
|
||||
|
||||
;; One of the more powerful features of tempo templates are automatic
|
||||
;; completion. With every template can be assigned a special tag that
|
||||
;; should be recognized by `tempo-complete-tag' and expanded to the
|
||||
;; complete template. By default the tags are added to a global list
|
||||
;; of template tags, and are matched against the last word before
|
||||
;; point. But if you assign your tags to a specific list, you can also
|
||||
;; specify another method for matching text in the buffer against the
|
||||
;; tags. In the HTML mode, for instance, the tags are matched against
|
||||
;; the text between the last `<' and point.
|
||||
|
||||
;; When defining a template named `foo', a symbol named
|
||||
;; `tempo-template-foo' will be created whose value as a variable will
|
||||
;; be the template definition, and its function value will be an
|
||||
;; interactive function that inserts the template at the point.
|
||||
|
||||
;; Full documentation for tempo.el can be found on the World Wide Web
|
||||
;; at http://www.lysator.liu.se:7500/~davidk/tempo.html (not yet
|
||||
;; completed)
|
||||
|
||||
;; The latest tempo.el distribution can be fetched from
|
||||
;; ftp.lysator.liu.se in the directory /pub/emacs
|
||||
|
||||
;;; Code:
|
||||
|
||||
(provide 'tempo)
|
||||
|
||||
;;; Variables
|
||||
|
||||
(defvar tempo-interactive nil
|
||||
"*Prompt user for strings in templates.
|
||||
If this variable is non-nil, `tempo-insert' prompts the
|
||||
user for text to insert in the templates")
|
||||
|
||||
(defvar tempo-insert-string-functions nil
|
||||
"List of functions to run when inserting a string.
|
||||
Each function is called with a single arg, STRING." )
|
||||
|
||||
(defvar tempo-tags nil
|
||||
"An association list with tags and corresponding templates")
|
||||
|
||||
(defvar tempo-local-tags '((tempo-tags . nil))
|
||||
"A list of locally installed tag completion lists.
|
||||
|
||||
It is a association list where the car of every element is a symbol
|
||||
whose varable value is a template list. The cdr part, if non-nil, is a
|
||||
function or a regexp that defines the string to match. See the
|
||||
documentation for the function `tempo-complete-tag' for more info.
|
||||
|
||||
`tempo-tags' is always in the last position in this list.")
|
||||
|
||||
(defvar tempo-marks nil
|
||||
"A list of marks to jump to with `\\[tempo-forward-mark]' and `\\[tempo-backward-mark]'.")
|
||||
|
||||
(defvar tempo-default-match-finder "\\b\\([^\\b]*\\)\\="
|
||||
"The default regexp used to find the string to match against the tags.")
|
||||
|
||||
;; Make some variables local to every buffer
|
||||
|
||||
(make-variable-buffer-local 'tempo-marks)
|
||||
(make-variable-buffer-local 'tempo-local-tags)
|
||||
|
||||
;;; Functions
|
||||
|
||||
;;
|
||||
;; tempo-define-template
|
||||
|
||||
(defun tempo-define-template (name elements &optional tag documentation taglist)
|
||||
"Define a template.
|
||||
This function creates a template variable `tempo-template-NAME' and an
|
||||
interactive function `tempo-template-NAME' that inserts the template
|
||||
at the point. The created function is returned.
|
||||
|
||||
NAME is a string that contains the name of the template, ELEMENTS is a
|
||||
list of elements in the template, TAG is the tag used for completion,
|
||||
DOCUMENTATION is the documentation string for the insertion command
|
||||
created, and TAGLIST (a symbol) is the tag list that TAG (if provided)
|
||||
should be added to). If TAGLIST is nil and TAG is non-nil, TAG is
|
||||
added to `tempo-tags'
|
||||
|
||||
The elements in ELEMENTS can be of several types:
|
||||
|
||||
- A string. It is sent to the hooks in `tempo-insert-string-functions',
|
||||
and the result is inserted.
|
||||
- The symbol 'p. This position is saved in `tempo-marks'.
|
||||
- The symbol 'r. If `tempo-insert' is called with ON-REGION non-nil
|
||||
the current region is placed here. Otherwise it works like 'p.
|
||||
- (p . PROMPT) If `tempo-interactive' is non-nil, the user is
|
||||
prompted in the minbuffer with PROMPT for a string to be inserted.
|
||||
If `tempo-interactive is nil, it works like 'p.
|
||||
- (r . PROMPT) like the previou, but if `tempo-interactive' is nil
|
||||
and `tempo-insert' is called with ON-REGION non-nil, the current
|
||||
region is placed here.
|
||||
- '& If there is only whitespace between the line start and point,
|
||||
nothing happens. Otherwise a newline is inserted.
|
||||
- '% If there is only whitespace between point and end-of-line
|
||||
nothing happens. Otherwise a newline is inserted.
|
||||
- 'n inserts a newline.
|
||||
- '> The line is indented using `indent-according-to-mode'. Note that
|
||||
you often should place this item after the text you want on the
|
||||
line.
|
||||
- 'n> inserts a newline and indents line.
|
||||
- nil. It is ignored.
|
||||
- Anything else. It is evaluated and the result is parsed again."
|
||||
|
||||
(let* ((template-name (intern (concat "tempo-template-"
|
||||
name)))
|
||||
(command-name template-name))
|
||||
(set template-name elements)
|
||||
(fset command-name (list 'lambda (list '&optional 'arg)
|
||||
(or documentation
|
||||
(concat "Insert a " name "."))
|
||||
(list 'interactive "*P")
|
||||
(list 'tempo-insert-template (list 'quote
|
||||
template-name)
|
||||
'arg)))
|
||||
(and tag
|
||||
(tempo-add-tag tag template-name taglist))
|
||||
command-name))
|
||||
|
||||
;;;
|
||||
;;; tempo-insert-template
|
||||
|
||||
(defun tempo-insert-template (template on-region)
|
||||
"Insert a template.
|
||||
TEMPLATE is the template to be inserted. If ON-REGION is non-nil the
|
||||
`r' elements are replaced with the current region."
|
||||
(and on-region
|
||||
(< (mark) (point))
|
||||
(exchange-point-and-mark))
|
||||
(save-excursion
|
||||
(tempo-insert-mark (point-marker))
|
||||
(mapcar 'tempo-insert
|
||||
(symbol-value template))
|
||||
(tempo-insert-mark (point-marker)))
|
||||
(tempo-forward-mark))
|
||||
|
||||
;;;
|
||||
;;; tempo-insert
|
||||
|
||||
(defun tempo-insert (element)
|
||||
"Insert a template element.
|
||||
Insert one element from a template. See documentation for
|
||||
`tempo-define-template' for the kind of elements possible."
|
||||
(cond ((stringp element) (tempo-process-and-insert-string element))
|
||||
((and (consp element) (eq (car element) 'p))
|
||||
(tempo-insert-prompt (cdr element)))
|
||||
((and (consp element) (eq (car element) 'r))
|
||||
(if on-region
|
||||
(exchange-point-and-mark)
|
||||
(tempo-insert-prompt (cdr element))))
|
||||
((eq element 'p) (tempo-insert-mark (point-marker)))
|
||||
((eq element 'r) (if on-region
|
||||
(exchange-point-and-mark)
|
||||
(tempo-insert-mark (point-marker))))
|
||||
((eq element '>) (indent-according-to-mode))
|
||||
((eq element '&) (if (not (or (= (current-column) 0)
|
||||
(save-excursion
|
||||
(re-search-backward
|
||||
"^\\s-*\\=" nil t))))
|
||||
(insert "\n")))
|
||||
((eq element '%) (if (not (or (eolp)
|
||||
(save-excursion
|
||||
(re-search-forward
|
||||
"\\=\\s-*$" nil t))))
|
||||
(insert "\n")))
|
||||
((eq element 'n) (insert "\n"))
|
||||
((eq element 'n>) (insert "\n") (indent-according-to-mode))
|
||||
((null element))
|
||||
(t (tempo-insert (eval element)))))
|
||||
|
||||
;;;
|
||||
;;; tempo-insert-prompt
|
||||
|
||||
(defun tempo-insert-prompt (prompt)
|
||||
"Prompt for a text string and insert it in the current buffer.
|
||||
If the variable `tempo-interactive' is non-nil the user is prompted
|
||||
for a string in the minibuffer, which is then inserted in the current
|
||||
buffer. If `tempo-interactive' is nil, the current point is placed on
|
||||
`tempo-forward-mark-list'.
|
||||
|
||||
PROMPT is the prompt string."
|
||||
(if tempo-interactive
|
||||
(insert (read-string prompt))
|
||||
(tempo-insert-mark (point-marker))))
|
||||
|
||||
;;;
|
||||
;;; tempo-process-and-insert-string
|
||||
|
||||
(defun tempo-process-and-insert-string (string)
|
||||
"Insert a string from a template.
|
||||
Run a string through the preprocessors in `tempo-insert-string-functions'
|
||||
and insert the results."
|
||||
|
||||
(cond ((null tempo-insert-string-functions)
|
||||
nil)
|
||||
((symbolp tempo-insert-string-functions)
|
||||
(setq string
|
||||
(apply tempo-insert-string-functions (list string))))
|
||||
((listp tempo-insert-string-functions)
|
||||
(mapcar (function (lambda (fn)
|
||||
(setq string (apply fn string))))
|
||||
tempo-insert-string-functions))
|
||||
(t
|
||||
(error "Bogus value in tempo-insert-string-functions: %s"
|
||||
tempo-insert-string-functions)))
|
||||
(insert string))
|
||||
|
||||
;;;
|
||||
;;; tempo-insert-mark
|
||||
|
||||
(defun tempo-insert-mark (mark)
|
||||
"Insert a mark `tempo-marks' while keeping it sorted"
|
||||
(cond ((null tempo-marks) (setq tempo-marks (list mark)))
|
||||
((< mark (car tempo-marks)) (setq tempo-marks (cons mark tempo-marks)))
|
||||
(t (let ((lp tempo-marks))
|
||||
(while (and (cdr lp)
|
||||
(<= (car (cdr lp)) mark))
|
||||
(setq lp (cdr lp)))
|
||||
(if (not (= mark (car lp)))
|
||||
(setcdr lp (cons mark (cdr lp))))))))
|
||||
|
||||
;;;
|
||||
;;; tempo-forward-mark
|
||||
|
||||
(defun tempo-forward-mark ()
|
||||
"Jump to the next mark in `tempo-forward-mark-list'."
|
||||
(interactive)
|
||||
(let ((next-mark (catch 'found
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (mark)
|
||||
(if (< (point) mark)
|
||||
(throw 'found mark))))
|
||||
tempo-marks)
|
||||
;; return nil if not found
|
||||
nil)))
|
||||
(if next-mark
|
||||
(goto-char next-mark))))
|
||||
|
||||
;;;
|
||||
;;; tempo-backward-mark
|
||||
|
||||
(defun tempo-backward-mark ()
|
||||
"Jump to the previous mark in `tempo-back-mark-list'."
|
||||
(interactive)
|
||||
(let ((prev-mark (catch 'found
|
||||
(let (last)
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (mark)
|
||||
(if (<= (point) mark)
|
||||
(throw 'found last))
|
||||
(setq last mark)))
|
||||
tempo-marks)
|
||||
last))))
|
||||
(if prev-mark
|
||||
(goto-char prev-mark))))
|
||||
|
||||
;;;
|
||||
;;; tempo-add-tag
|
||||
|
||||
(defun tempo-add-tag (tag template &optional tag-list)
|
||||
"Add a template tag.
|
||||
|
||||
Add the TAG, that should complete to TEMPLATE to the list in TAG-LIST,
|
||||
or to `tempo-tags' if TAG-LIST is nil."
|
||||
|
||||
(interactive "sTag: \nCTemplate: ")
|
||||
(if (null tag-list)
|
||||
(setq tag-list 'tempo-tags))
|
||||
(if (not (assoc tag (symbol-value tag-list)))
|
||||
(set tag-list (cons (cons tag template) (symbol-value tag-list)))))
|
||||
|
||||
;;;
|
||||
;;; tempo-use-tag-list
|
||||
|
||||
(defun tempo-use-tag-list (tag-list &optional completion-function)
|
||||
"Install TAG-LIST to be used for template completion in the current buffer.
|
||||
|
||||
TAG-LIST is a symbol whose variable value is a tag list created with
|
||||
`tempo-add-tag' and COMPLETION-FUNCTION is an optional function or
|
||||
string that is used by `\\[tempo-complete-tag]' to find a string to
|
||||
match the tag against.
|
||||
|
||||
If COMPLETION-FUNCTION is a string, it should contain a regular
|
||||
expression with at least one \\( \\) pair. When searching for tags,
|
||||
`tempo-complete-tag' calls `re-search-backward' with this string, and
|
||||
the string between the first \\( and \\) is used for matching against
|
||||
each string in the tag list. If one is found, the whole text between
|
||||
the first \\( and the point is replaced with the inserted template.
|
||||
|
||||
You will probably want to include \\ \= at the end of the regexp to make
|
||||
sure that the string is matched only against text adjacent to the
|
||||
point.
|
||||
|
||||
If COPMLETION-FUNCTION is a symbol, it should be a function that
|
||||
returns a cons cell of the form (STRING . POS), where STRING is the
|
||||
string used for matching and POS is the buffer position after which
|
||||
text should be replaced with a template."
|
||||
|
||||
(let ((old (assq tag-list tempo-local-tags)))
|
||||
(if old
|
||||
(setcdr old completion-function)
|
||||
(setq tempo-local-tags (cons (cons tag-list completion-function)
|
||||
tempo-local-tags)))))
|
||||
|
||||
;;;
|
||||
;;; tempo-find-match-string
|
||||
|
||||
(defun tempo-find-match-string (finder)
|
||||
"Find a string to be matched against a tag list.
|
||||
|
||||
FINDER is a function or a string. Returns (STRING . POS)."
|
||||
(cond ((stringp finder)
|
||||
(save-excursion
|
||||
(re-search-backward finder nil t))
|
||||
(cons (buffer-substring (match-beginning 1) (1+ (match-end 1)))
|
||||
(match-beginning 1)))
|
||||
(t
|
||||
(funcall finder))))
|
||||
|
||||
;;;
|
||||
;;; tempo-complete-tag
|
||||
|
||||
(defun tempo-complete-tag (&optional silent)
|
||||
"Look for a tag and expand it..
|
||||
|
||||
It goes through the tag lists in `tempo-local-tags' (this includes
|
||||
`tempo-tags') and for each list it uses the corresponding match-finder
|
||||
function, or `tempo-default-match-finder' if none is given, and tries
|
||||
to match the match string against the tags in the list using
|
||||
`try-completion'. If none is found it proceeds to the next list until
|
||||
one is found. If a partial completion is found, it is replaced by the
|
||||
template if it can be completed uniquely, or completed as far as
|
||||
possible.
|
||||
|
||||
When doing partial completion, only tags in the currently examined
|
||||
list are considered, so if you provide similar tags in different lists
|
||||
in `tempo-local-tags', the result may not be desirable.
|
||||
|
||||
If no match is found or a partial match is found, and SILENT is
|
||||
non-nil, the function will give a signal."
|
||||
|
||||
(interactive)
|
||||
(if (catch 'completed
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (tag-list-a)
|
||||
(let* ((tag-list (symbol-value(car tag-list-a)))
|
||||
(match-string-finder (or (cdr tag-list-a)
|
||||
tempo-default-match-finder))
|
||||
(match-info (tempo-find-match-string match-string-finder))
|
||||
(match-string (car match-info))
|
||||
(match-start (cdr match-info))
|
||||
(compl (or (cdr (assoc match-string tag-list))
|
||||
(try-completion (car match-info)
|
||||
tag-list))))
|
||||
|
||||
(if compl ;any match
|
||||
(delete-region match-start (point)))
|
||||
|
||||
(cond
|
||||
((null compl)
|
||||
nil)
|
||||
((symbolp compl)
|
||||
(tempo-insert-template compl nil)
|
||||
(throw 'completed t))
|
||||
((eq compl t)
|
||||
(tempo-insert-template (cdr (assoc match-string tag-list))
|
||||
nil)
|
||||
(throw 'completed t))
|
||||
((stringp compl)
|
||||
(let ((compl2 (assoc compl tag-list)))
|
||||
(if compl2
|
||||
(tempo-insert-template (cdr compl2) nil)
|
||||
(insert compl)
|
||||
(if (string= match-string compl)
|
||||
(if (not silent)
|
||||
(ding)))))
|
||||
(throw 'completed t))))))
|
||||
tempo-local-tags)
|
||||
;; No completion found. Return nil
|
||||
nil)
|
||||
;; Do nothing if a completion was found
|
||||
t
|
||||
;; No completion was found
|
||||
(if (not silent)
|
||||
(ding))
|
||||
nil))
|
||||
|
||||
;;; tempo.el ends here
|
Loading…
Add table
Reference in a new issue