root/trunk/lisp/emulation/viper-util.el

Revision 4220, 53.9 kB (checked in by miyoshi, 9 months ago)

Sync up with Emacs22.2.

  • Property svn:eol-style set to LF
Line 
1 ;;; viper-util.el --- Utilities used by viper.el
2
3 ;; Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2002, 2003,
4 ;;   2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5
6 ;; Author: Michael Kifer <kifer@cs.stonybrook.edu>
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 3, or (at your option)
13 ;; any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 ;; Boston, MA 02110-1301, USA.
24
25 ;;; Commentary:
26
27 ;;; Code:
28
29 ;; Compiler pacifier
30 (defvar viper-overriding-map)
31 (defvar pm-color-alist)
32 (defvar zmacs-region-stays)
33 (defvar viper-minibuffer-current-face)
34 (defvar viper-minibuffer-insert-face)
35 (defvar viper-minibuffer-vi-face)
36 (defvar viper-minibuffer-emacs-face)
37 (defvar viper-replace-overlay-face)
38 (defvar viper-fast-keyseq-timeout)
39 (defvar ex-unix-type-shell)
40 (defvar ex-unix-type-shell-options)
41 (defvar viper-ex-tmp-buf-name)
42 (defvar viper-syntax-preference)
43 (defvar viper-saved-mark)
44
45 (require 'ring)
46
47 (if noninteractive
48     (eval-when-compile
49       (let ((load-path (cons (expand-file-name ".") load-path)))
50         (or (featurep 'viper-init)
51             (load "viper-init.el" nil nil 'nosuffix))
52         )))
53 ;; end pacifier
54
55 (require 'viper-init)
56
57
58 ;; A fix for NeXT Step
59 ;; Should go away, when NS people fix the design flaw, which leaves the
60 ;; two x-* functions undefined.
61 (if (and (not (fboundp 'x-display-color-p)) (fboundp 'ns-display-color-p))
62     (fset 'x-display-color-p (symbol-function 'ns-display-color-p)))
63 (if (and (not (fboundp 'x-color-defined-p)) (fboundp 'ns-color-defined-p))
64       (fset 'x-color-defined-p (symbol-function 'ns-color-defined-p)))
65
66
67 ;;; XEmacs support
68
69
70 (viper-cond-compile-for-xemacs-or-emacs
71  (progn ; xemacs
72    (fset 'viper-overlay-p (symbol-function 'extentp))
73    (fset 'viper-make-overlay (symbol-function 'make-extent))
74    (fset 'viper-overlay-live-p (symbol-function 'extent-live-p))
75    (fset 'viper-move-overlay (symbol-function 'set-extent-endpoints))
76    (fset 'viper-overlay-start (symbol-function 'extent-start-position))
77    (fset 'viper-overlay-end (symbol-function 'extent-end-position))
78    (fset 'viper-overlay-get (symbol-function 'extent-property))
79    (fset 'viper-overlay-put (symbol-function 'set-extent-property))
80    (fset 'viper-read-event (symbol-function 'next-command-event))
81    (fset 'viper-characterp (symbol-function 'characterp))
82    (fset 'viper-int-to-char (symbol-function 'int-to-char))
83    (if (viper-window-display-p)
84        (fset 'viper-iconify (symbol-function 'iconify-frame)))
85    (cond ((viper-has-face-support-p)
86           (fset 'viper-get-face (symbol-function 'get-face))
87           (fset 'viper-color-defined-p (symbol-function 'valid-color-name-p))
88           )))
89  (progn ; emacs
90    (fset 'viper-overlay-p (symbol-function 'overlayp))
91    (fset 'viper-make-overlay (symbol-function 'make-overlay))
92    (fset 'viper-overlay-live-p (symbol-function 'overlayp))
93    (fset 'viper-move-overlay (symbol-function 'move-overlay))
94    (fset 'viper-overlay-start (symbol-function 'overlay-start))
95    (fset 'viper-overlay-end (symbol-function 'overlay-end))
96    (fset 'viper-overlay-get (symbol-function 'overlay-get))
97    (fset 'viper-overlay-put (symbol-function 'overlay-put))
98    (fset 'viper-read-event (symbol-function 'read-event))
99    (fset 'viper-characterp (symbol-function 'integerp))
100    (fset 'viper-int-to-char (symbol-function 'identity))
101    (if (viper-window-display-p)
102        (fset 'viper-iconify (symbol-function 'iconify-or-deiconify-frame)))
103    (cond ((viper-has-face-support-p)
104           (fset 'viper-get-face (symbol-function 'internal-get-face))
105           (fset 'viper-color-defined-p (symbol-function 'x-color-defined-p))
106           )))
107  )
108
109
110
111 ;; CHAR is supposed to be a char or an integer (positive or negative)
112 ;; LIST is a list of chars, nil, and negative numbers
113 ;; Check if CHAR is a member by trying to convert in characters, if necessary.
114 ;; Introduced for compatibility with XEmacs, where integers are not the same as
115 ;; chars.
116 (defun viper-memq-char (char list)
117   (cond ((and (integerp char) (>= char 0))
118          (memq (viper-int-to-char char) list))
119         ((memq char list))))
120
121 ;; Check if char-or-int and char are the same as characters
122 (defun viper-char-equal (char-or-int char)
123   (cond ((and (integerp char-or-int) (>= char-or-int 0))
124          (= (viper-int-to-char char-or-int) char))
125         ((eq char-or-int char))))
126
127 ;; Like =, but accommodates null and also is t for eq-objects
128 (defun viper= (char char1)
129   (cond ((eq char char1) t)
130         ((and (viper-characterp char) (viper-characterp char1))
131          (= char char1))
132         (t nil)))
133
134 (defsubst viper-color-display-p ()
135   (viper-cond-compile-for-xemacs-or-emacs
136    (eq (device-class (selected-device)) 'color) ; xemacs
137    (x-display-color-p)  ; emacs
138    ))
139
140 (defun viper-get-cursor-color (&optional frame)
141   (viper-cond-compile-for-xemacs-or-emacs
142    (color-instance-name
143     (frame-property (or frame (selected-frame)) 'cursor-color)) ; xemacs
144    (cdr (assoc 'cursor-color (frame-parameters))) ; emacs
145    ))
146
147
148 ;; OS/2
149 (cond ((eq (viper-device-type) 'pm)
150        (fset 'viper-color-defined-p
151              (lambda (color) (assoc color pm-color-alist)))))
152
153
154 ;; cursor colors
155 (defun viper-change-cursor-color (new-color &optional frame)
156   (if (and (viper-window-display-p)  (viper-color-display-p)
157            (stringp new-color) (viper-color-defined-p new-color)
158            (not (string= new-color (viper-get-cursor-color))))
159       (viper-cond-compile-for-xemacs-or-emacs
160        (set-frame-property
161         (or frame (selected-frame))
162         'cursor-color (make-color-instance new-color))
163        (modify-frame-parameters
164         (or frame (selected-frame))
165         (list (cons 'cursor-color new-color)))
166        )
167     ))
168
169 (defun viper-set-cursor-color-according-to-state (&optional frame)
170   (cond ((eq viper-current-state 'replace-state)
171          (viper-change-cursor-color viper-replace-state-cursor-color frame))
172         ((and (eq viper-current-state 'emacs-state)
173               viper-emacs-state-cursor-color)
174          (viper-change-cursor-color viper-emacs-state-cursor-color frame))
175         ((eq viper-current-state 'insert-state)
176          (viper-change-cursor-color viper-insert-state-cursor-color frame))
177         (t
178          (viper-change-cursor-color viper-vi-state-cursor-color frame))))
179
180 ;; By default, saves current frame cursor color in the
181 ;; viper-saved-cursor-color-in-replace-mode property of viper-replace-overlay
182 (defun viper-save-cursor-color (before-which-mode)
183   (if (and (viper-window-display-p) (viper-color-display-p))
184       (let ((color (viper-get-cursor-color)))
185         (if (and (stringp color) (viper-color-defined-p color)
186                  (not (string= color viper-replace-overlay-cursor-color)))
187             (modify-frame-parameters
188              (selected-frame)
189              (list
190               (cons
191                (cond ((eq before-which-mode 'before-replace-mode)
192                       'viper-saved-cursor-color-in-replace-mode)
193                      ((eq before-which-mode 'before-emacs-mode)
194                       'viper-saved-cursor-color-in-emacs-mode)
195                      (t
196                       'viper-saved-cursor-color-in-insert-mode))
197                color)))
198           ))))
199
200
201 (defsubst viper-get-saved-cursor-color-in-replace-mode ()
202   (or
203    (funcall
204     (if viper-emacs-p 'frame-parameter 'frame-property)
205     (selected-frame)
206     'viper-saved-cursor-color-in-replace-mode)
207    (if (and (eq viper-current-state 'emacs-mode) viper-emacs-state-cursor-color)
208        viper-emacs-state-cursor-color
209      viper-vi-state-cursor-color)))
210
211 (defsubst viper-get-saved-cursor-color-in-insert-mode ()
212   (or
213    (funcall
214     (if viper-emacs-p 'frame-parameter 'frame-property)
215     (selected-frame)
216     'viper-saved-cursor-color-in-insert-mode)
217    (if (and (eq viper-current-state 'emacs-mode) viper-emacs-state-cursor-color)
218        viper-emacs-state-cursor-color
219      viper-vi-state-cursor-color)))
220
221 (defsubst viper-get-saved-cursor-color-in-emacs-mode ()
222   (or
223    (funcall
224     (if viper-emacs-p 'frame-parameter 'frame-property)
225     (selected-frame)
226     'viper-saved-cursor-color-in-emacs-mode)
227    viper-vi-state-cursor-color))
228
229 ;; restore cursor color from replace overlay
230 (defun viper-restore-cursor-color(after-which-mode)
231   (if (viper-overlay-p viper-replace-overlay)
232       (viper-change-cursor-color
233        (cond ((eq after-which-mode 'after-replace-mode)
234               (viper-get-saved-cursor-color-in-replace-mode))
235              ((eq after-which-mode 'after-emacs-mode)
236               (viper-get-saved-cursor-color-in-emacs-mode))
237              (t (viper-get-saved-cursor-color-in-insert-mode)))
238        )))
239
240
241 ;; Check the current version against the major and minor version numbers
242 ;; using op: cur-vers op major.minor If emacs-major-version or
243 ;; emacs-minor-version are not defined, we assume that the current version
244 ;; is hopelessly outdated.  We assume that emacs-major-version and
245 ;; emacs-minor-version are defined.  Otherwise, for Emacs/XEmacs 19, if the
246 ;; current minor version is < 10 (xemacs) or < 23 (emacs) the return value
247 ;; will be nil (when op is =, >, or >=) and t (when op is <, <=), which may be
248 ;; incorrect.  However, this gives correct result in our cases, since we are
249 ;; testing for sufficiently high Emacs versions.
250 (defun viper-check-version (op major minor &optional type-of-emacs)
251   (if (and (boundp 'emacs-major-version) (boundp 'emacs-minor-version))
252       (and (cond ((eq type-of-emacs 'xemacs) viper-xemacs-p)
253                  ((eq type-of-emacs 'emacs) viper-emacs-p)
254                  (t t))
255            (cond ((eq op '=) (and (= emacs-minor-version minor)
256                                   (= emacs-major-version major)))
257                  ((memq op '(> >= < <=))
258                   (and (or (funcall op emacs-major-version major)
259                            (= emacs-major-version major))
260                        (if (= emacs-major-version major)
261                            (funcall op emacs-minor-version minor)
262                          t)))
263                  (t
264                   (error "%S: Invalid op in viper-check-version" op))))
265     (cond ((memq op '(= > >=)) nil)
266           ((memq op '(< <=)) t))))
267
268
269 (defun viper-get-visible-buffer-window (wind)
270   (if viper-xemacs-p
271       (get-buffer-window wind t)
272     (get-buffer-window wind 'visible)))
273
274
275 ;; Return line position.
276 ;; If pos is 'start then returns position of line start.
277 ;; If pos is 'end, returns line end.  If pos is 'mid, returns line center.
278 ;; Pos = 'indent returns beginning of indentation.
279 ;; Otherwise, returns point.  Current point is not moved in any case."
280 (defun viper-line-pos (pos)
281   (let ((cur-pos (point))
282         (result))
283     (cond
284      ((equal pos 'start)
285       (beginning-of-line))
286      ((equal pos 'end)
287       (end-of-line))
288      ((equal pos 'mid)
289       (goto-char (+ (viper-line-pos 'start) (viper-line-pos 'end) 2)))
290      ((equal pos 'indent)
291       (back-to-indentation))
292      (t   nil))
293     (setq result (point))
294     (goto-char cur-pos)
295     result))
296
297 ;; Emacs used to count each multibyte character as several positions in the buffer,
298 ;; so we had to use Emacs' chars-in-region to count characters. Since 20.3,
299 ;; Emacs counts multibyte characters as 1 position.  XEmacs has always been
300 ;; counting each char as just one pos. So, now we can simply subtract beg from
301 ;; end to determine the number of characters in a region.
302 (defun viper-chars-in-region (beg end &optional preserve-sign)
303   ;;(let ((count (abs (if (fboundp 'chars-in-region)
304   ;;                    (chars-in-region beg end)
305   ;;                  (- end beg)))))
306   (let ((count (abs (- end beg))))
307     (if (and (< end beg) preserve-sign)
308         (- count)
309       count)))
310
311 ;; Test if POS is between BEG and END
312 (defsubst viper-pos-within-region (pos beg end)
313   (and (>= pos (min beg end)) (>= (max beg end) pos)))
314
315
316 ;; Like move-marker but creates a virgin marker if arg isn't already a marker.
317 ;; The first argument must eval to a variable name.
318 ;; Arguments: (var-name position &optional buffer).
319 ;;
320 ;; This is useful for moving markers that are supposed to be local.
321 ;; For this, VAR-NAME should be made buffer-local with nil as a default.
322 ;; Then, each time this var is used in `viper-move-marker-locally' in a new
323 ;; buffer, a new marker will be created.
324 (defun viper-move-marker-locally (var pos &optional buffer)
325   (if (markerp (eval var))
326       ()
327     (set var (make-marker)))
328   (move-marker (eval var) pos buffer))
329
330
331 ;; Print CONDITIONS as a message.
332 (defun viper-message-conditions (conditions)
333   (let ((case (car conditions)) (msg (cdr conditions)))
334     (if (null msg)
335         (message "%s" case)
336       (message "%s: %s" case (mapconcat 'prin1-to-string msg " ")))
337     (beep 1)))
338
339
340
341 ;;; List/alist utilities
342
343 ;; Convert LIST to an alist
344 (defun viper-list-to-alist (lst)
345   (let ((alist))
346     (while lst
347       (setq alist (cons (list (car lst)) alist))
348       (setq lst (cdr lst)))
349     alist))
350
351 ;; Convert ALIST to a list.
352 (defun viper-alist-to-list (alst)
353   (let ((lst))
354     (while alst
355       (setq lst (cons (car (car alst)) lst))
356       (setq alst (cdr alst)))
357     lst))
358
359 ;; Filter ALIST using REGEXP.  Return alist whose elements match the regexp.
360 (defun viper-filter-alist (regexp alst)
361   (interactive "s x")
362   (let ((outalst) (inalst alst))
363     (while (car inalst)
364       (if (string-match regexp (car (car inalst)))
365           (setq outalst (cons (car inalst) outalst)))
366       (setq inalst (cdr inalst)))
367     outalst))
368
369 ;; Filter LIST using REGEXP.  Return list whose elements match the regexp.
370 (defun viper-filter-list (regexp lst)
371   (interactive "s x")
372   (let ((outlst) (inlst lst))
373     (while (car inlst)
374       (if (string-match regexp (car inlst))
375           (setq outlst (cons (car inlst) outlst)))
376       (setq inlst (cdr inlst)))
377     outlst))
378
379
380 ;; Append LIS2 to LIS1, both alists, by side-effect and returns LIS1
381 ;; LIS2 is modified by filtering it: deleting its members of the form
382 ;; \(car elt\) such that (car elt') is in LIS1.
383 (defun viper-append-filter-alist (lis1 lis2)
384   (let ((temp lis1)
385         elt)
386     ;;filter-append the second list
387     (while temp
388       ;; delete all occurrences
389       (while (setq elt (assoc (car (car temp)) lis2))
390         (setq lis2 (delq elt lis2)))
391       (setq temp (cdr temp)))
392
393     (append lis1 lis2)))
394
395
396
397 ;;; Support for :e, :r, :w file globbing
398
399 ;; Glob the file spec.
400 ;; This function is designed to work under Unix.  It might also work under VMS.
401 (defun viper-glob-unix-files (filespec)
402   (let ((gshell
403          (cond (ex-unix-type-shell shell-file-name)
404                ((memq system-type '(vax-vms axp-vms)) "*dcl*") ; VAX VMS
405                (t "sh"))) ; probably Unix anyway
406         (gshell-options
407          ;; using cond in anticipation of further additions
408          (cond (ex-unix-type-shell-options)
409                ))
410         (command (cond (viper-ms-style-os-p (format "\"ls -1 -d %s\"" filespec))
411                        (t (format "ls -1 -d %s" filespec))))
412         status)
413     (save-excursion
414       (set-buffer (get-buffer-create viper-ex-tmp-buf-name))
415       (erase-buffer)
416       (setq status
417             (if gshell-options
418                 (call-process gshell nil t nil
419                               gshell-options
420                               "-c"
421                               command)
422               (call-process gshell nil t nil
423                             "-c"
424                             command)))
425       (goto-char (point-min))
426       ;; Issue an error, if no match.
427       (unless (eq 0 status)
428         (save-excursion
429           (skip-chars-forward " \t\n\j")
430           (if (looking-at "ls:")
431               (viper-forward-Word 1))
432           (error "%s: %s"
433                  (if (stringp  gshell)
434                      gshell
435                    "shell")
436                  (buffer-substring (point) (viper-line-pos 'end)))
437           ))
438       (goto-char (point-min))
439       (viper-get-filenames-from-buffer 'one-per-line))
440     ))
441
442
443 ;; Interpret the stuff in the buffer as a list of file names
444 ;; return a list of file names listed in the buffer beginning at point
445 ;; If optional arg is supplied, assume each filename is listed on a separate
446 ;; line
447 (defun viper-get-filenames-from-buffer (&optional one-per-line)
448   (let ((skip-chars (if one-per-line "\t\n" " \t\n"))
449          result fname delim)
450     (skip-chars-forward skip-chars)
451     (while (not (eobp))
452       (if (cond ((looking-at "\"")
453                  (setq delim ?\")
454                  (re-search-forward "[^\"]+" nil t)) ; noerror
455                 ((looking-at "'")
456                  (setq delim ?')
457                  (re-search-forward "[^']+" nil t)) ; noerror
458                 (t
459                  (re-search-forward
460                   (concat "[^" skip-chars "]+") nil t))) ;noerror
461           (setq fname
462                 (buffer-substring (match-beginning 0) (match-end 0))))
463       (if delim
464           (forward-char 1))
465       (skip-chars-forward " \t\n")
466       (setq result (cons fname result)))
467     result))
468
469 ;; convert MS-DOS wildcards to regexp
470 (defun viper-wildcard-to-regexp (wcard)
471   (save-excursion
472     (set-buffer (get-buffer-create viper-ex-tmp-buf-name))
473     (erase-buffer)
474     (insert wcard)
475     (goto-char (point-min))
476     (while (not (eobp))
477       (skip-chars-forward "^*?.\\\\")
478       (cond ((eq (char-after (point)) ?*) (insert ".")(forward-char 1))
479             ((eq (char-after (point)) ?.) (insert "\\")(forward-char 1))
480             ((eq (char-after (point)) ?\\) (insert "\\")(forward-char 1))
481             ((eq (char-after (point)) ??) (delete-char 1)(insert ".")))
482       )
483     (buffer-string)
484     ))
485
486
487 ;; glob windows files
488 ;; LIST is expected to be in reverse order
489 (defun viper-glob-mswindows-files (filespec)
490   (let ((case-fold-search t)
491         tmp tmp2)
492     (save-excursion
493       (set-buffer (get-buffer-create viper-ex-tmp-buf-name))
494       (erase-buffer)
495       (insert filespec)
496       (goto-char (point-min))
497       (setq tmp (viper-get-filenames-from-buffer))
498       (while tmp
499         (setq tmp2 (cons (directory-files
500                           ;; the directory part
501                           (or (file-name-directory (car tmp))
502                               "")
503                           t  ; return full names
504                           ;; the regexp part: globs the file names
505                           (concat "^"
506                                   (viper-wildcard-to-regexp
507                                    (file-name-nondirectory (car tmp)))
508                                   "$"))
509                          tmp2))
510         (setq tmp (cdr tmp)))
511       (reverse (apply 'append tmp2)))))
512
513
514 ;;; Insertion ring
515
516 ;; Rotate RING's index.  DIRection can be positive or negative.
517 (defun viper-ring-rotate1 (ring dir)
518   (if (and (ring-p ring) (> (ring-length ring) 0))
519       (progn
520         (setcar ring (cond ((> dir 0)
521                             (ring-plus1 (car ring) (ring-length ring)))
522                            ((< dir 0)
523                             (ring-minus1 (car ring) (ring-length ring)))
524                            ;; don't rotate if dir = 0
525                            (t (car ring))))
526         (viper-current-ring-item ring)
527         )))
528
529 (defun viper-special-ring-rotate1 (ring dir)
530   (if (memq viper-intermediate-command
531             '(repeating-display-destructive-command
532               repeating-insertion-from-ring))
533       (viper-ring-rotate1 ring dir)
534     ;; don't rotate otherwise
535     (viper-ring-rotate1 ring 0)))
536
537 ;; current ring item; if N is given, then so many items back from the
538 ;; current
539 (defun viper-current-ring-item (ring &optional n)
540   (setq n (or n 0))
541   (if (and (ring-p ring) (> (ring-length ring) 0))
542       (aref (cdr (cdr ring)) (mod (- (car ring) 1 n) (ring-length ring)))))
543
544 ;; Push item onto ring.  The second argument is a ring-variable, not value.
545 (defun viper-push-onto-ring (item ring-var)
546   (or (ring-p (eval ring-var))
547       (set ring-var (make-ring (eval (intern (format "%S-size" ring-var))))))
548   (or (null item) ; don't push nil
549       (and (stringp item) (string= item "")) ; or empty strings
550       (equal item (viper-current-ring-item (eval ring-var))) ; or old stuff
551       ;; Since viper-set-destructive-command checks if we are inside
552       ;; viper-repeat, we don't check whether this-command-keys is a `.'.  The
553       ;; cmd viper-repeat makes a call to the current function only if `.' is
554       ;; executing a command from the command history.  It doesn't call the
555       ;; push-onto-ring function if `.' is simply repeating the last
556       ;; destructive command.  We only check for ESC (which happens when we do
557       ;; insert with a prefix argument, or if this-command-keys doesn't give
558       ;; anything meaningful (in that case we don't know what to show to the
559       ;; user).
560       (and (eq ring-var 'viper-command-ring)
561            (string-match "\\([0-9]*\e\\|^[ \t]*$\\|escape\\)"
562                          (viper-array-to-string (this-command-keys))))
563       (viper-ring-insert (eval ring-var) item))
564   )
565
566
567 ;; removing elts from ring seems to break it
568 (defun viper-cleanup-ring (ring)
569   (or (< (ring-length ring) 2)
570       (null (viper-current-ring-item ring))
571       ;; last and previous equal
572       (if (equal (viper-current-ring-item ring)
573                  (viper-current-ring-item ring 1))
574           (viper-ring-pop ring))))
575
576 ;; ring-remove seems to be buggy, so we concocted this for our purposes.
577 (defun viper-ring-pop (ring)
578   (let* ((ln (ring-length ring))
579          (vec (cdr (cdr ring)))
580          (veclen (length vec))
581          (hd (car ring))
582          (idx (max 0 (ring-minus1 hd ln)))
583          (top-elt (aref vec idx)))
584
585         ;; shift elements
586         (while (< (1+ idx) veclen)
587           (aset vec idx (aref vec (1+ idx)))
588           (setq idx (1+ idx)))
589         (aset vec idx nil)
590
591         (setq hd (max 0 (ring-minus1 hd ln)))
592         (if (= hd (1- ln)) (setq hd 0))
593         (setcar ring hd) ; move head
594         (setcar (cdr ring) (max 0 (1- ln))) ; adjust length
595         top-elt
596         ))
597
598 (defun viper-ring-insert (ring item)
599   (let* ((ln (ring-length ring))
600          (vec (cdr (cdr ring)))
601          (veclen (length vec))
602          (hd (car ring))
603          (vecpos-after-hd (if (= hd 0) ln hd))
604          (idx ln))
605
606     (if (= ln veclen)
607         (progn
608           (aset vec hd item) ; hd is always 1+ the actual head index in vec
609           (setcar ring (ring-plus1 hd ln)))
610       (setcar (cdr ring) (1+ ln))
611       (setcar ring (ring-plus1 vecpos-after-hd (1+ ln)))
612       (while (and (>= idx vecpos-after-hd) (> ln 0))
613         (aset vec idx (aref vec (1- idx)))
614         (setq idx (1- idx)))
615       (aset vec vecpos-after-hd item))
616     item))
617
618
619 ;;; String utilities
620
621 ;; If STRING is longer than MAX-LEN, truncate it and print ...... instead
622 ;; PRE-STRING is a string to prepend to the abbrev string.
623 ;; POST-STRING is a string to append to the abbrev string.
624 ;; ABBREV_SIGN is a string to be inserted before POST-STRING
625 ;; if the orig string was truncated.
626 (defun viper-abbreviate-string (string max-len
627                                      pre-string post-string abbrev-sign)
628   (let (truncated-str)
629     (setq truncated-str
630           (if (stringp string)
631               (substring string 0 (min max-len (length string)))))
632     (cond ((null truncated-str) "")
633           ((> (length string) max-len)
634            (format "%s%s%s%s"
635                    pre-string truncated-str abbrev-sign post-string))
636           (t (format "%s%s%s" pre-string truncated-str post-string)))))
637
638 ;; tells if we are over a whitespace-only line
639 (defsubst viper-over-whitespace-line ()
640   (save-excursion
641     (beginning-of-line)
642     (looking-at "^[ \t]*$")))
643
644
645 ;;; Saving settings in custom file
646
647 ;; Save the current setting of VAR in CUSTOM-FILE.
648 ;; If given, MESSAGE is a message to be displayed after that.
649 ;; This message is erased after 2 secs, if erase-msg is non-nil.
650 ;; Arguments: var message custom-file &optional erase-message
651 (defun viper-save-setting (var message custom-file &optional erase-msg)
652   (let* ((var-name (symbol-name var))
653          (var-val (if (boundp var) (eval var)))
654          (regexp (format "^[^;]*%s[ \t\n]*[a-zA-Z---_']*[ \t\n)]" var-name))
655          (buf (find-file-noselect (substitute-in-file-name custom-file)))
656         )
657     (message message)
658     (save-excursion
659       (set-buffer buf)
660       (goto-char (point-min))
661       (if (re-search-forward regexp nil t)
662           (let ((reg-end (1- (match-end 0))))
663             (search-backward var-name)
664             (delete-region (match-beginning 0) reg-end)
665             (goto-char (match-beginning 0))
666             (insert (format "%s  '%S" var-name var-val)))
667         (goto-char (point-max))
668         (if (not (bolp)) (insert "\n"))
669         (insert (format "(setq %s '%S)\n" var-name var-val)))
670       (save-buffer))
671       (kill-buffer buf)
672       (if erase-msg
673           (progn
674             (sit-for 2)
675             (message "")))
676       ))
677
678 ;; Save STRING in CUSTOM-FILE.  If PATTERN is non-nil, remove strings that
679 ;; match this pattern.
680 (defun viper-save-string-in-file (string custom-file &optional pattern)
681   (let ((buf (find-file-noselect (substitute-in-file-name custom-file))))
682     (save-excursion
683       (set-buffer buf)
684       (let (buffer-read-only)
685         (goto-char (point-min))
686         (if pattern (delete-matching-lines pattern))
687         (goto-char (point-max))
688         (if string (insert string))
689         (save-buffer)))
690     (kill-buffer buf)
691     ))
692
693
694 ;; define remote file test
695 (defun viper-file-remote-p (file-name)
696   (file-remote-p file-name))
697
698
699 ;; This is a simple-minded check for whether a file is under version control.
700 ;; If file,v exists but file doesn't, this file is considered to be not checked
701 ;; in and not checked out for the purpose of patching (since patch won't be
702 ;; able to read such a file anyway).
703 ;; FILE is a string representing file name
704 ;;(defun viper-file-under-version-control (file)
705 ;;  (let* ((filedir (file-name-directory file))
706 ;;       (file-nondir (file-name-nondirectory file))
707 ;;       (trial (concat file-nondir ",v"))
708 ;;       (full-trial (concat filedir trial))
709 ;;       (full-rcs-trial (concat filedir "RCS/" trial)))
710 ;;    (and (stringp file)
711 ;;       (file-exists-p file)
712 ;;       (or
713 ;;        (and
714 ;;         (file-exists-p full-trial)
715 ;;         ;; in FAT FS, `file,v' and `file' may turn out to be the same!
716 ;;         ;; don't be fooled by this!
717 ;;         (not (equal (file-attributes file)
718 ;;                     (file-attributes full-trial))))
719 ;;        ;; check if a version is in RCS/ directory
720 ;;        (file-exists-p full-rcs-trial)))
721 ;;       ))
722
723
724 (defsubst viper-file-checked-in-p (file)
725   (and (featurep 'vc-hooks)
726        ;; CVS files are considered not checked in
727        (not (memq (vc-backend file) '(nil CVS)))
728        (if (fboundp 'vc-state)
729            (and
730              (not (memq (vc-state file) '(edited needs-merge)))
731              (not (stringp (vc-state file))))
732          ;; XEmacs has no vc-state
733          (not (vc-locking-user file)))
734        ))
735
736 ;; checkout if visited file is checked in
737 (defun viper-maybe-checkout (buf)
738   (let ((file (expand-file-name (buffer-file-name buf)))
739         (checkout-function (key-binding "\C-x\C-q")))
740     (if (and (viper-file-checked-in-p file)
741              (or (beep 1) t)
742              (y-or-n-p
743               (format
744                "File %s is checked in.  Check it out? "
745                (viper-abbreviate-file-name file))))
746         (with-current-buffer buf
747           (command-execute checkout-function)))))
748
749
750
751
752 ;;; Overlays
753 (defun viper-put-on-search-overlay (beg end)
754   (if (viper-overlay-p viper-search-overlay)
755       (viper-move-overlay viper-search-overlay beg end)
756     (setq viper-search-overlay (viper-make-overlay beg end (current-buffer)))
757     (viper-overlay-put
758      viper-search-overlay 'priority viper-search-overlay-priority))
759   (viper-overlay-put viper-search-overlay 'face viper-search-face))
760
761 ;; Search
762
763 (defun viper-flash-search-pattern ()
764   (if (not (viper-has-face-support-p))
765       nil
766     (viper-put-on-search-overlay (match-beginning 0) (match-end 0))
767     (sit-for 2)
768     (viper-overlay-put viper-search-overlay 'face nil)))
769
770 (defun viper-hide-search-overlay ()
771   (if (not (viper-overlay-p viper-search-overlay))
772       (progn
773         (setq viper-search-overlay
774               (viper-make-overlay (point-min) (point-min) (current-buffer)))
775         (viper-overlay-put
776          viper-search-overlay 'priority viper-search-overlay-priority)))
777   (viper-overlay-put viper-search-overlay 'face nil))
778
779 ;; Replace state
780
781 (defsubst viper-move-replace-overlay (beg end)
782   (viper-move-overlay viper-replace-overlay beg end))
783
784 (defun viper-set-replace-overlay (beg end)
785   (if (viper-overlay-live-p viper-replace-overlay)
786       (viper-move-replace-overlay beg end)
787     (setq viper-replace-overlay (viper-make-overlay beg end (current-buffer)))
788     ;; never detach
789     (viper-overlay-put
790      viper-replace-overlay (if viper-emacs-p 'evaporate 'detachable) nil)
791     (viper-overlay-put
792      viper-replace-overlay 'priority viper-replace-overlay-priority)
793     ;; If Emacs will start supporting overlay maps, as it currently supports
794     ;; text-property maps, we could do away with viper-replace-minor-mode and
795     ;; just have keymap attached to replace overlay.
796     ;;(viper-overlay-put
797     ;; viper-replace-overlay
798     ;; (if viper-xemacs-p 'keymap 'local-map)
799     ;; viper-replace-map)
800     )
801   (if (viper-has-face-support-p)
802       (viper-overlay-put
803        viper-replace-overlay 'face viper-replace-overlay-face))
804   (viper-save-cursor-color 'before-replace-mode)
805   (viper-change-cursor-color viper-replace-overlay-cursor-color)
806   )
807
808
809 (defun viper-set-replace-overlay-glyphs (before-glyph after-glyph)
810   (or (viper-overlay-live-p viper-replace-overlay)
811       (viper-set-replace-overlay (point-min) (point-min)))
812   (if (or (not (viper-has-face-support-p))
813           viper-use-replace-region-delimiters)
814       (let ((before-name (if viper-xemacs-p 'begin-glyph 'before-string))
815             (after-name (if viper-xemacs-p 'end-glyph 'after-string)))
816         (viper-overlay-put viper-replace-overlay before-name before-glyph)
817         (viper-overlay-put viper-replace-overlay after-name after-glyph))))
818
819 (defun viper-hide-replace-overlay ()
820   (viper-set-replace-overlay-glyphs nil nil)
821   (viper-restore-cursor-color 'after-replace-mode)
822   (viper-restore-cursor-color 'after-insert-mode)
823   (if (viper-has-face-support-p)
824       (viper-overlay-put viper-replace-overlay 'face nil)))
825
826
827 (defsubst viper-replace-start ()
828   (viper-overlay-start viper-replace-overlay))
829 (defsubst viper-replace-end ()
830   (viper-overlay-end viper-replace-overlay))
831
832
833 ;; Minibuffer
834
835 (defun viper-set-minibuffer-overlay ()
836   (viper-check-minibuffer-overlay)
837   (if (viper-has-face-support-p)
838       (progn
839         (viper-overlay-put
840          viper-minibuffer-overlay 'face viper-minibuffer-current-face)
841         (viper-overlay-put
842          viper-minibuffer-overlay 'priority viper-minibuffer-overlay-priority)
843         ;; never detach
844         (viper-overlay-put
845          viper-minibuffer-overlay
846          (if viper-emacs-p 'evaporate 'detachable)
847          nil)
848         ;; make viper-minibuffer-overlay open-ended
849         ;; In emacs, it is made open ended at creation time
850         (if viper-xemacs-p
851             (progn
852               (viper-overlay-put viper-minibuffer-overlay 'start-open nil)
853               (viper-overlay-put viper-minibuffer-overlay 'end-open nil)))
854         )))
855
856 (defun viper-check-minibuffer-overlay ()
857