Fix previous change to include scalable/mimetypes directory.
[emacs.git] / lisp / emulation / viper-util.el
blob291c03c4c4e511642413099f58ac3f906647fe94
1 ;;; viper-util.el --- Utilities used by viper.el
3 ;; Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2002, 2003,
4 ;; 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
6 ;; Author: Michael Kifer <kifer@cs.stonybrook.edu>
8 ;; This file is part of GNU Emacs.
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.
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.
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.
25 ;;; Commentary:
27 ;;; Code:
29 ;; Compiler pacifier
30 (defvar viper-overriding-map)
31 (defvar pm-color-alist)
32 (defvar viper-minibuffer-current-face)
33 (defvar viper-minibuffer-insert-face)
34 (defvar viper-minibuffer-vi-face)
35 (defvar viper-minibuffer-emacs-face)
36 (defvar viper-replace-overlay-face)
37 (defvar viper-fast-keyseq-timeout)
38 (defvar ex-unix-type-shell)
39 (defvar ex-unix-type-shell-options)
40 (defvar viper-ex-tmp-buf-name)
41 (defvar viper-syntax-preference)
42 (defvar viper-saved-mark)
44 (require 'ring)
46 (eval-and-compile
47 (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
49 ;; end pacifier
51 (require 'viper-init)
54 ;; A fix for NeXT Step
55 ;; Should go away, when NS people fix the design flaw, which leaves the
56 ;; two x-* functions undefined.
57 (if (and (not (fboundp 'x-display-color-p)) (fboundp 'ns-display-color-p))
58 (fset 'x-display-color-p (symbol-function 'ns-display-color-p)))
59 (if (and (not (fboundp 'x-color-defined-p)) (fboundp 'ns-color-defined-p))
60 (fset 'x-color-defined-p (symbol-function 'ns-color-defined-p)))
63 (defalias 'viper-overlay-p
64 (if (featurep 'xemacs) 'extentp 'overlayp))
65 (defalias 'viper-make-overlay
66 (if (featurep 'xemacs) 'make-extent 'make-overlay))
67 (defalias 'viper-overlay-live-p
68 (if (featurep 'xemacs) 'extent-live-p 'overlayp))
69 (defalias 'viper-move-overlay
70 (if (featurep 'xemacs) 'set-extent-endpoints 'move-overlay))
71 (defalias 'viper-overlay-start
72 (if (featurep 'xemacs) 'extent-start-position 'overlay-start))
73 (defalias 'viper-overlay-end
74 (if (featurep 'xemacs) 'extent-end-position 'overlay-end))
75 (defalias 'viper-overlay-get
76 (if (featurep 'xemacs) 'extent-property 'overlay-get))
77 (defalias 'viper-overlay-put
78 (if (featurep 'xemacs) 'set-extent-property 'overlay-put))
79 (defalias 'viper-read-event
80 (if (featurep 'xemacs) 'next-command-event 'read-event))
81 (defalias 'viper-characterp
82 (if (featurep 'xemacs) 'characterp 'integerp))
83 (defalias 'viper-int-to-char
84 (if (featurep 'xemacs) 'int-to-char 'identity))
85 (defalias 'viper-get-face
86 (if (featurep 'xemacs) 'get-face 'internal-get-face))
87 (defalias 'viper-color-defined-p
88 (if (featurep 'xemacs) 'valid-color-name-p 'x-color-defined-p))
89 (defalias 'viper-iconify
90 (if (featurep 'xemacs) 'iconify-frame 'iconify-or-deiconify-frame))
93 ;; CHAR is supposed to be a char or an integer (positive or negative)
94 ;; LIST is a list of chars, nil, and negative numbers
95 ;; Check if CHAR is a member by trying to convert in characters, if necessary.
96 ;; Introduced for compatibility with XEmacs, where integers are not the same as
97 ;; chars.
98 (defun viper-memq-char (char list)
99 (cond ((and (integerp char) (>= char 0))
100 (memq (viper-int-to-char char) list))
101 ((memq char list))))
103 ;; Check if char-or-int and char are the same as characters
104 (defun viper-char-equal (char-or-int char)
105 (cond ((and (integerp char-or-int) (>= char-or-int 0))
106 (= (viper-int-to-char char-or-int) char))
107 ((eq char-or-int char))))
109 ;; Like =, but accommodates null and also is t for eq-objects
110 (defun viper= (char char1)
111 (cond ((eq char char1) t)
112 ((and (viper-characterp char) (viper-characterp char1))
113 (= char char1))
114 (t nil)))
116 (defsubst viper-color-display-p ()
117 (if (featurep 'xemacs) (eq (device-class (selected-device)) 'color)
118 (x-display-color-p)))
120 (defun viper-get-cursor-color (&optional frame)
121 (if (featurep 'xemacs)
122 (color-instance-name
123 (frame-property (or frame (selected-frame)) 'cursor-color))
124 (cdr (assoc 'cursor-color (frame-parameters)))))
126 (defmacro viper-frame-value (variable)
127 "Return the value of VARIABLE local to the current frame, if there is one.
128 Otherwise return the normal value."
129 `(if (featurep 'xemacs)
130 ,variable
131 ;; Frame-local variables are obsolete from Emacs 22.2 onwards,
132 ;; so we do it by hand instead.
133 ;; Buffer-local values take precedence over frame-local ones.
134 (if (local-variable-p ',variable)
135 ,variable
136 ;; Distinguish between no frame parameter and a frame parameter
137 ;; with a value of nil.
138 (let ((fp (assoc ',variable (frame-parameters))))
139 (if fp (cdr fp)
140 ,variable)))))
142 ;; OS/2
143 (cond ((eq (viper-device-type) 'pm)
144 (fset 'viper-color-defined-p
145 (lambda (color) (assoc color pm-color-alist)))))
148 ;; cursor colors
149 (defun viper-change-cursor-color (new-color &optional frame)
150 (if (and (viper-window-display-p) (viper-color-display-p)
151 (stringp new-color) (viper-color-defined-p new-color)
152 (not (string= new-color (viper-get-cursor-color))))
153 (if (featurep 'xemacs)
154 (set-frame-property
155 (or frame (selected-frame))
156 'cursor-color (make-color-instance new-color))
157 (modify-frame-parameters
158 (or frame (selected-frame))
159 (list (cons 'cursor-color new-color))))))
161 ;; Note that the colors this function uses might not be those
162 ;; associated with FRAME, if there are frame-local values.
163 ;; This was equally true before the advent of viper-frame-value.
164 ;; Now it could be changed by passing frame to v-f-v.
165 (defun viper-set-cursor-color-according-to-state (&optional frame)
166 (cond ((eq viper-current-state 'replace-state)
167 (viper-change-cursor-color
168 (viper-frame-value viper-replace-overlay-cursor-color)
169 frame))
170 ((and (eq viper-current-state 'emacs-state)
171 (viper-frame-value viper-emacs-state-cursor-color))
172 (viper-change-cursor-color
173 (viper-frame-value viper-emacs-state-cursor-color)
174 frame))
175 ((eq viper-current-state 'insert-state)
176 (viper-change-cursor-color
177 (viper-frame-value viper-insert-state-cursor-color)
178 frame))
180 (viper-change-cursor-color
181 (viper-frame-value viper-vi-state-cursor-color)
182 frame))))
184 ;; By default, saves current frame cursor color in the
185 ;; viper-saved-cursor-color-in-replace-mode property of viper-replace-overlay
186 (defun viper-save-cursor-color (before-which-mode)
187 (if (and (viper-window-display-p) (viper-color-display-p))
188 (let ((color (viper-get-cursor-color)))
189 (if (and (stringp color) (viper-color-defined-p color)
190 (not (string= color
191 (viper-frame-value
192 viper-replace-overlay-cursor-color))))
193 (modify-frame-parameters
194 (selected-frame)
195 (list
196 (cons
197 (cond ((eq before-which-mode 'before-replace-mode)
198 'viper-saved-cursor-color-in-replace-mode)
199 ((eq before-which-mode 'before-emacs-mode)
200 'viper-saved-cursor-color-in-emacs-mode)
202 'viper-saved-cursor-color-in-insert-mode))
203 color)))))))
206 (defsubst viper-get-saved-cursor-color-in-replace-mode ()
208 (funcall
209 (if (featurep 'emacs) 'frame-parameter 'frame-property)
210 (selected-frame)
211 'viper-saved-cursor-color-in-replace-mode)
212 (let ((ecolor (viper-frame-value viper-emacs-state-cursor-color)))
213 (or (and (eq viper-current-state 'emacs-mode)
214 ecolor)
215 (viper-frame-value viper-vi-state-cursor-color)))))
217 (defsubst viper-get-saved-cursor-color-in-insert-mode ()
219 (funcall
220 (if (featurep 'emacs) 'frame-parameter 'frame-property)
221 (selected-frame)
222 'viper-saved-cursor-color-in-insert-mode)
223 (let ((ecolor (viper-frame-value viper-emacs-state-cursor-color)))
224 (or (and (eq viper-current-state 'emacs-mode)
225 ecolor)
226 (viper-frame-value viper-vi-state-cursor-color)))))
228 (defsubst viper-get-saved-cursor-color-in-emacs-mode ()
230 (funcall
231 (if (featurep 'emacs) 'frame-parameter 'frame-property)
232 (selected-frame)
233 'viper-saved-cursor-color-in-emacs-mode)
234 (viper-frame-value viper-vi-state-cursor-color)))
236 ;; restore cursor color from replace overlay
237 (defun viper-restore-cursor-color(after-which-mode)
238 (if (viper-overlay-p viper-replace-overlay)
239 (viper-change-cursor-color
240 (cond ((eq after-which-mode 'after-replace-mode)
241 (viper-get-saved-cursor-color-in-replace-mode))
242 ((eq after-which-mode 'after-emacs-mode)
243 (viper-get-saved-cursor-color-in-emacs-mode))
244 (t (viper-get-saved-cursor-color-in-insert-mode)))
248 ;; Check the current version against the major and minor version numbers
249 ;; using op: cur-vers op major.minor If emacs-major-version or
250 ;; emacs-minor-version are not defined, we assume that the current version
251 ;; is hopelessly outdated. We assume that emacs-major-version and
252 ;; emacs-minor-version are defined. Otherwise, for Emacs/XEmacs 19, if the
253 ;; current minor version is < 10 (xemacs) or < 23 (emacs) the return value
254 ;; will be nil (when op is =, >, or >=) and t (when op is <, <=), which may be
255 ;; incorrect. However, this gives correct result in our cases, since we are
256 ;; testing for sufficiently high Emacs versions.
257 (defun viper-check-version (op major minor &optional type-of-emacs)
258 (if (and (boundp 'emacs-major-version) (boundp 'emacs-minor-version))
259 (and (cond ((eq type-of-emacs 'xemacs) (featurep 'xemacs))
260 ((eq type-of-emacs 'emacs) (featurep 'emacs))
261 (t t))
262 (cond ((eq op '=) (and (= emacs-minor-version minor)
263 (= emacs-major-version major)))
264 ((memq op '(> >= < <=))
265 (and (or (funcall op emacs-major-version major)
266 (= emacs-major-version major))
267 (if (= emacs-major-version major)
268 (funcall op emacs-minor-version minor)
269 t)))
271 (error "%S: Invalid op in viper-check-version" op))))
272 (cond ((memq op '(= > >=)) nil)
273 ((memq op '(< <=)) t))))
276 (defun viper-get-visible-buffer-window (wind)
277 (if (featurep 'xemacs)
278 (get-buffer-window wind t)
279 (get-buffer-window wind 'visible)))
282 ;; Return line position.
283 ;; If pos is 'start then returns position of line start.
284 ;; If pos is 'end, returns line end. If pos is 'mid, returns line center.
285 ;; Pos = 'indent returns beginning of indentation.
286 ;; Otherwise, returns point. Current point is not moved in any case."
287 (defun viper-line-pos (pos)
288 (let ((cur-pos (point))
289 (result))
290 (cond
291 ((equal pos 'start)
292 (beginning-of-line))
293 ((equal pos 'end)
294 (end-of-line))
295 ((equal pos 'mid)
296 (goto-char (+ (viper-line-pos 'start) (viper-line-pos 'end) 2)))
297 ((equal pos 'indent)
298 (back-to-indentation))
299 (t nil))
300 (setq result (point))
301 (goto-char cur-pos)
302 result))
304 ;; Emacs used to count each multibyte character as several positions in the buffer,
305 ;; so we had to use Emacs' chars-in-region to count characters. Since 20.3,
306 ;; Emacs counts multibyte characters as 1 position. XEmacs has always been
307 ;; counting each char as just one pos. So, now we can simply subtract beg from
308 ;; end to determine the number of characters in a region.
309 (defun viper-chars-in-region (beg end &optional preserve-sign)
310 ;;(let ((count (abs (if (fboundp 'chars-in-region)
311 ;; (chars-in-region beg end)
312 ;; (- end beg)))))
313 (let ((count (abs (- end beg))))
314 (if (and (< end beg) preserve-sign)
315 (- count)
316 count)))
318 ;; Test if POS is between BEG and END
319 (defsubst viper-pos-within-region (pos beg end)
320 (and (>= pos (min beg end)) (>= (max beg end) pos)))
323 ;; Like move-marker but creates a virgin marker if arg isn't already a marker.
324 ;; The first argument must eval to a variable name.
325 ;; Arguments: (var-name position &optional buffer).
327 ;; This is useful for moving markers that are supposed to be local.
328 ;; For this, VAR-NAME should be made buffer-local with nil as a default.
329 ;; Then, each time this var is used in `viper-move-marker-locally' in a new
330 ;; buffer, a new marker will be created.
331 (defun viper-move-marker-locally (var pos &optional buffer)
332 (if (markerp (eval var))
334 (set var (make-marker)))
335 (move-marker (eval var) pos buffer))
338 ;; Print CONDITIONS as a message.
339 (defun viper-message-conditions (conditions)
340 (let ((case (car conditions)) (msg (cdr conditions)))
341 (if (null msg)
342 (message "%s" case)
343 (message "%s: %s" case (mapconcat 'prin1-to-string msg " ")))
344 (beep 1)))
348 ;;; List/alist utilities
350 ;; Convert LIST to an alist
351 (defun viper-list-to-alist (lst)
352 (let ((alist))
353 (while lst
354 (setq alist (cons (list (car lst)) alist))
355 (setq lst (cdr lst)))
356 alist))
358 ;; Convert ALIST to a list.
359 (defun viper-alist-to-list (alst)
360 (let ((lst))
361 (while alst
362 (setq lst (cons (car (car alst)) lst))
363 (setq alst (cdr alst)))
364 lst))
366 ;; Filter ALIST using REGEXP. Return alist whose elements match the regexp.
367 (defun viper-filter-alist (regexp alst)
368 (interactive "s x")
369 (let ((outalst) (inalst alst))
370 (while (car inalst)
371 (if (string-match regexp (car (car inalst)))
372 (setq outalst (cons (car inalst) outalst)))
373 (setq inalst (cdr inalst)))
374 outalst))
376 ;; Filter LIST using REGEXP. Return list whose elements match the regexp.
377 (defun viper-filter-list (regexp lst)
378 (interactive "s x")
379 (let ((outlst) (inlst lst))
380 (while (car inlst)
381 (if (string-match regexp (car inlst))
382 (setq outlst (cons (car inlst) outlst)))
383 (setq inlst (cdr inlst)))
384 outlst))
387 ;; Append LIS2 to LIS1, both alists, by side-effect and returns LIS1
388 ;; LIS2 is modified by filtering it: deleting its members of the form
389 ;; \(car elt\) such that (car elt') is in LIS1.
390 (defun viper-append-filter-alist (lis1 lis2)
391 (let ((temp lis1)
392 elt)
393 ;;filter-append the second list
394 (while temp
395 ;; delete all occurrences
396 (while (setq elt (assoc (car (car temp)) lis2))
397 (setq lis2 (delq elt lis2)))
398 (setq temp (cdr temp)))
400 (append lis1 lis2)))
404 (declare-function viper-forward-Word "viper-cmd" (arg))
406 ;;; Support for :e, :r, :w file globbing
408 ;; Glob the file spec.
409 ;; This function is designed to work under Unix. It might also work under VMS.
410 (defun viper-glob-unix-files (filespec)
411 (let ((gshell
412 (cond (ex-unix-type-shell shell-file-name)
413 ((memq system-type '(vax-vms axp-vms)) "*dcl*") ; VAX VMS
414 (t "sh"))) ; probably Unix anyway
415 (gshell-options
416 ;; using cond in anticipation of further additions
417 (cond (ex-unix-type-shell-options)
419 (command (cond (viper-ms-style-os-p (format "\"ls -1 -d %s\"" filespec))
420 (t (format "ls -1 -d %s" filespec))))
421 status)
422 (save-excursion
423 (set-buffer (get-buffer-create viper-ex-tmp-buf-name))
424 (erase-buffer)
425 (setq status
426 (if gshell-options
427 (call-process gshell nil t nil
428 gshell-options
429 "-c"
430 command)
431 (call-process gshell nil t nil
432 "-c"
433 command)))
434 (goto-char (point-min))
435 ;; Issue an error, if no match.
436 (unless (eq 0 status)
437 (save-excursion
438 (skip-chars-forward " \t\n\j")
439 (if (looking-at "ls:")
440 (viper-forward-Word 1))
441 (error "%s: %s"
442 (if (stringp gshell)
443 gshell
444 "shell")
445 (buffer-substring (point) (viper-line-pos 'end)))
447 (goto-char (point-min))
448 (viper-get-filenames-from-buffer 'one-per-line))
452 ;; Interpret the stuff in the buffer as a list of file names
453 ;; return a list of file names listed in the buffer beginning at point
454 ;; If optional arg is supplied, assume each filename is listed on a separate
455 ;; line
456 (defun viper-get-filenames-from-buffer (&optional one-per-line)
457 (let ((skip-chars (if one-per-line "\t\n" " \t\n"))
458 result fname delim)
459 (skip-chars-forward skip-chars)
460 (while (not (eobp))
461 (if (cond ((looking-at "\"")
462 (setq delim ?\")
463 (re-search-forward "[^\"]+" nil t)) ; noerror
464 ((looking-at "'")
465 (setq delim ?')
466 (re-search-forward "[^']+" nil t)) ; noerror
468 (re-search-forward
469 (concat "[^" skip-chars "]+") nil t))) ;noerror
470 (setq fname
471 (buffer-substring (match-beginning 0) (match-end 0))))
472 (if delim
473 (forward-char 1))
474 (skip-chars-forward " \t\n")
475 (setq result (cons fname result)))
476 result))
478 ;; convert MS-DOS wildcards to regexp
479 (defun viper-wildcard-to-regexp (wcard)
480 (save-excursion
481 (set-buffer (get-buffer-create viper-ex-tmp-buf-name))
482 (erase-buffer)
483 (insert wcard)
484 (goto-char (point-min))
485 (while (not (eobp))
486 (skip-chars-forward "^*?.\\\\")
487 (cond ((eq (char-after (point)) ?*) (insert ".")(forward-char 1))
488 ((eq (char-after (point)) ?.) (insert "\\")(forward-char 1))
489 ((eq (char-after (point)) ?\\) (insert "\\")(forward-char 1))
490 ((eq (char-after (point)) ??) (delete-char 1)(insert ".")))
492 (buffer-string)
496 ;; glob windows files
497 ;; LIST is expected to be in reverse order
498 (defun viper-glob-mswindows-files (filespec)
499 (let ((case-fold-search t)
500 tmp tmp2)
501 (save-excursion
502 (set-buffer (get-buffer-create viper-ex-tmp-buf-name))
503 (erase-buffer)
504 (insert filespec)
505 (goto-char (point-min))
506 (setq tmp (viper-get-filenames-from-buffer))
507 (while tmp
508 (setq tmp2 (cons (directory-files
509 ;; the directory part
510 (or (file-name-directory (car tmp))
512 t ; return full names
513 ;; the regexp part: globs the file names
514 (concat "^"
515 (viper-wildcard-to-regexp
516 (file-name-nondirectory (car tmp)))
517 "$"))
518 tmp2))
519 (setq tmp (cdr tmp)))
520 (reverse (apply 'append tmp2)))))
523 ;;; Insertion ring
525 ;; Rotate RING's index. DIRection can be positive or negative.
526 (defun viper-ring-rotate1 (ring dir)
527 (if (and (ring-p ring) (> (ring-length ring) 0))
528 (progn
529 (setcar ring (cond ((> dir 0)
530 (ring-plus1 (car ring) (ring-length ring)))
531 ((< dir 0)
532 (ring-minus1 (car ring) (ring-length ring)))
533 ;; don't rotate if dir = 0
534 (t (car ring))))
535 (viper-current-ring-item ring)
538 (defun viper-special-ring-rotate1 (ring dir)
539 (if (memq viper-intermediate-command
540 '(repeating-display-destructive-command
541 repeating-insertion-from-ring))
542 (viper-ring-rotate1 ring dir)
543 ;; don't rotate otherwise
544 (viper-ring-rotate1 ring 0)))
546 ;; current ring item; if N is given, then so many items back from the
547 ;; current
548 (defun viper-current-ring-item (ring &optional n)
549 (setq n (or n 0))
550 (if (and (ring-p ring) (> (ring-length ring) 0))
551 (aref (cdr (cdr ring)) (mod (- (car ring) 1 n) (ring-length ring)))))
553 ;; Push item onto ring. The second argument is a ring-variable, not value.
554 (defun viper-push-onto-ring (item ring-var)
555 (or (ring-p (eval ring-var))
556 (set ring-var (make-ring (eval (intern (format "%S-size" ring-var))))))
557 (or (null item) ; don't push nil
558 (and (stringp item) (string= item "")) ; or empty strings
559 (equal item (viper-current-ring-item (eval ring-var))) ; or old stuff
560 ;; Since viper-set-destructive-command checks if we are inside
561 ;; viper-repeat, we don't check whether this-command-keys is a `.'. The
562 ;; cmd viper-repeat makes a call to the current function only if `.' is
563 ;; executing a command from the command history. It doesn't call the
564 ;; push-onto-ring function if `.' is simply repeating the last
565 ;; destructive command. We only check for ESC (which happens when we do
566 ;; insert with a prefix argument, or if this-command-keys doesn't give
567 ;; anything meaningful (in that case we don't know what to show to the
568 ;; user).
569 (and (eq ring-var 'viper-command-ring)
570 (string-match "\\([0-9]*\e\\|^[ \t]*$\\|escape\\)"
571 (viper-array-to-string (this-command-keys))))
572 (viper-ring-insert (eval ring-var) item))
576 ;; removing elts from ring seems to break it
577 (defun viper-cleanup-ring (ring)
578 (or (< (ring-length ring) 2)
579 (null (viper-current-ring-item ring))
580 ;; last and previous equal
581 (if (equal (viper-current-ring-item ring)
582 (viper-current-ring-item ring 1))
583 (viper-ring-pop ring))))
585 ;; ring-remove seems to be buggy, so we concocted this for our purposes.
586 (defun viper-ring-pop (ring)
587 (let* ((ln (ring-length ring))
588 (vec (cdr (cdr ring)))
589 (veclen (length vec))
590 (hd (car ring))
591 (idx (max 0 (ring-minus1 hd ln)))
592 (top-elt (aref vec idx)))
594 ;; shift elements
595 (while (< (1+ idx) veclen)
596 (aset vec idx (aref vec (1+ idx)))
597 (setq idx (1+ idx)))
598 (aset vec idx nil)
600 (setq hd (max 0 (ring-minus1 hd ln)))
601 (if (= hd (1- ln)) (setq hd 0))
602 (setcar ring hd) ; move head
603 (setcar (cdr ring) (max 0 (1- ln))) ; adjust length
604 top-elt
607 (defun viper-ring-insert (ring item)
608 (let* ((ln (ring-length ring))
609 (vec (cdr (cdr ring)))
610 (veclen (length vec))
611 (hd (car ring))
612 (vecpos-after-hd (if (= hd 0) ln hd))
613 (idx ln))
615 (if (= ln veclen)
616 (progn
617 (aset vec hd item) ; hd is always 1+ the actual head index in vec
618 (setcar ring (ring-plus1 hd ln)))
619 (setcar (cdr ring) (1+ ln))
620 (setcar ring (ring-plus1 vecpos-after-hd (1+ ln)))
621 (while (and (>= idx vecpos-after-hd) (> ln 0))
622 (aset vec idx (aref vec (1- idx)))
623 (setq idx (1- idx)))
624 (aset vec vecpos-after-hd item))
625 item))
628 ;;; String utilities
630 ;; If STRING is longer than MAX-LEN, truncate it and print ...... instead
631 ;; PRE-STRING is a string to prepend to the abbrev string.
632 ;; POST-STRING is a string to append to the abbrev string.
633 ;; ABBREV_SIGN is a string to be inserted before POST-STRING
634 ;; if the orig string was truncated.
635 (defun viper-abbreviate-string (string max-len
636 pre-string post-string abbrev-sign)
637 (let (truncated-str)
638 (setq truncated-str
639 (if (stringp string)
640 (substring string 0 (min max-len (length string)))))
641 (cond ((null truncated-str) "")
642 ((> (length string) max-len)
643 (format "%s%s%s%s"
644 pre-string truncated-str abbrev-sign post-string))
645 (t (format "%s%s%s" pre-string truncated-str post-string)))))
647 ;; tells if we are over a whitespace-only line
648 (defsubst viper-over-whitespace-line ()
649 (save-excursion
650 (beginning-of-line)
651 (looking-at "^[ \t]*$")))
654 ;;; Saving settings in custom file
656 ;; Save the current setting of VAR in CUSTOM-FILE.
657 ;; If given, MESSAGE is a message to be displayed after that.
658 ;; This message is erased after 2 secs, if erase-msg is non-nil.
659 ;; Arguments: var message custom-file &optional erase-message
660 (defun viper-save-setting (var message custom-file &optional erase-msg)
661 (let* ((var-name (symbol-name var))
662 (var-val (if (boundp var) (eval var)))
663 (regexp (format "^[^;]*%s[ \t\n]*[a-zA-Z---_']*[ \t\n)]" var-name))
664 (buf (find-file-noselect (substitute-in-file-name custom-file)))
666 (message "%s" (or message ""))
667 (save-excursion
668 (set-buffer buf)
669 (goto-char (point-min))
670 (if (re-search-forward regexp nil t)
671 (let ((reg-end (1- (match-end 0))))
672 (search-backward var-name)
673 (delete-region (match-beginning 0) reg-end)
674 (goto-char (match-beginning 0))
675 (insert (format "%s '%S" var-name var-val)))
676 (goto-char (point-max))
677 (if (not (bolp)) (insert "\n"))
678 (insert (format "(setq %s '%S)\n" var-name var-val)))
679 (save-buffer))
680 (kill-buffer buf)
681 (if erase-msg
682 (progn
683 (sit-for 2)
684 (message "")))
687 ;; Save STRING in CUSTOM-FILE. If PATTERN is non-nil, remove strings that
688 ;; match this pattern.
689 (defun viper-save-string-in-file (string custom-file &optional pattern)
690 (let ((buf (find-file-noselect (substitute-in-file-name custom-file))))
691 (save-excursion
692 (set-buffer buf)
693 (let (buffer-read-only)
694 (goto-char (point-min))
695 (if pattern (delete-matching-lines pattern))
696 (goto-char (point-max))
697 (if string (insert string))
698 (save-buffer)))
699 (kill-buffer buf)
703 ;; define remote file test
704 (defun viper-file-remote-p (file-name)
705 (file-remote-p file-name))
708 ;; This is a simple-minded check for whether a file is under version control.
709 ;; If file,v exists but file doesn't, this file is considered to be not checked
710 ;; in and not checked out for the purpose of patching (since patch won't be
711 ;; able to read such a file anyway).
712 ;; FILE is a string representing file name
713 ;;(defun viper-file-under-version-control (file)
714 ;; (let* ((filedir (file-name-directory file))
715 ;; (file-nondir (file-name-nondirectory file))
716 ;; (trial (concat file-nondir ",v"))
717 ;; (full-trial (concat filedir trial))
718 ;; (full-rcs-trial (concat filedir "RCS/" trial)))
719 ;; (and (stringp file)
720 ;; (file-exists-p file)
721 ;; (or
722 ;; (and
723 ;; (file-exists-p full-trial)
724 ;; ;; in FAT FS, `file,v' and `file' may turn out to be the same!
725 ;; ;; don't be fooled by this!
726 ;; (not (equal (file-attributes file)
727 ;; (file-attributes full-trial))))
728 ;; ;; check if a version is in RCS/ directory
729 ;; (file-exists-p full-rcs-trial)))
730 ;; ))
733 (defsubst viper-file-checked-in-p (file)
734 (and (featurep 'vc-hooks)
735 ;; CVS files are considered not checked in
736 ;; FIXME: Should this deal with more than CVS?
737 (not (memq (vc-backend file) '(nil CVS)))
738 (if (fboundp 'vc-state)
739 (and
740 (not (memq (vc-state file) '(edited needs-merge)))
741 (not (stringp (vc-state file))))
742 ;; XEmacs has no vc-state
743 (if (featurep 'xemacs) (not (vc-locking-user file))))))
745 ;; checkout if visited file is checked in
746 (defun viper-maybe-checkout (buf)
747 (let ((file (expand-file-name (buffer-file-name buf)))
748 (checkout-function (key-binding "\C-x\C-q")))
749 (if (and (viper-file-checked-in-p file)
750 (or (beep 1) t)
751 (y-or-n-p
752 (format
753 "File %s is checked in. Check it out? "
754 (viper-abbreviate-file-name file))))
755 (with-current-buffer buf
756 (command-execute checkout-function)))))
761 ;;; Overlays
762 (defun viper-put-on-search-overlay (beg end)
763 (if (viper-overlay-p viper-search-overlay)
764 (viper-move-overlay viper-search-overlay beg end)
765 (setq viper-search-overlay (viper-make-overlay beg end (current-buffer)))
766 (viper-overlay-put
767 viper-search-overlay 'priority viper-search-overlay-priority))
768 (viper-overlay-put viper-search-overlay 'face viper-search-face))
770 ;; Search
772 (defun viper-flash-search-pattern ()
773 (if (not (viper-has-face-support-p))
775 (viper-put-on-search-overlay (match-beginning 0) (match-end 0))
776 (sit-for 2)
777 (viper-overlay-put viper-search-overlay 'face nil)))
779 (defun viper-hide-search-overlay ()
780 (if (not (viper-overlay-p viper-search-overlay))
781 (progn
782 (setq viper-search-overlay
783 (viper-make-overlay (point-min) (point-min) (current-buffer)))
784 (viper-overlay-put
785 viper-search-overlay 'priority viper-search-overlay-priority)))
786 (viper-overlay-put viper-search-overlay 'face nil))
788 ;; Replace state
790 (defsubst viper-move-replace-overlay (beg end)
791 (viper-move-overlay viper-replace-overlay beg end))
793 (defun viper-set-replace-overlay (beg end)
794 (if (viper-overlay-live-p viper-replace-overlay)
795 (viper-move-replace-overlay beg end)
796 (setq viper-replace-overlay (viper-make-overlay beg end (current-buffer)))
797 ;; never detach
798 (viper-overlay-put
799 viper-replace-overlay (if (featurep 'emacs) 'evaporate 'detachable) nil)
800 (viper-overlay-put
801 viper-replace-overlay 'priority viper-replace-overlay-priority)
802 ;; If Emacs will start supporting overlay maps, as it currently supports
803 ;; text-property maps, we could do away with viper-replace-minor-mode and
804 ;; just have keymap attached to replace overlay.
805 ;;(viper-overlay-put
806 ;; viper-replace-overlay
807 ;; (if (featurep 'xemacs) 'keymap 'local-map)
808 ;; viper-replace-map)
810 (if (viper-has-face-support-p)
811 (viper-overlay-put
812 viper-replace-overlay 'face viper-replace-overlay-face))
813 (viper-save-cursor-color 'before-replace-mode)
814 (viper-change-cursor-color
815 (viper-frame-value viper-replace-overlay-cursor-color)))
818 (defun viper-set-replace-overlay-glyphs (before-glyph after-glyph)
819 (or (viper-overlay-live-p viper-replace-overlay)
820 (viper-set-replace-overlay (point-min) (point-min)))
821 (if (or (not (viper-has-face-support-p))
822 viper-use-replace-region-delimiters)
823 (let ((before-name (if (featurep 'xemacs) 'begin-glyph 'before-string))
824 (after-name (if (featurep 'xemacs) 'end-glyph 'after-string)))
825 (viper-overlay-put viper-replace-overlay before-name before-glyph)
826 (viper-overlay-put viper-replace-overlay after-name after-glyph))))
828 (defun viper-hide-replace-overlay ()
829 (viper-set-replace-overlay-glyphs nil nil)
830 (viper-restore-cursor-color 'after-replace-mode)
831 (viper-restore-cursor-color 'after-insert-mode)
832 (if (viper-has-face-support-p)
833 (viper-overlay-put viper-replace-overlay 'face nil)))
836 (defsubst viper-replace-start ()
837 (viper-overlay-start viper-replace-overlay))
838 (defsubst viper-replace-end ()
839 (viper-overlay-end viper-replace-overlay))
842 ;; Minibuffer
844 (defun viper-set-minibuffer-overlay ()
845 (viper-check-minibuffer-overlay)
846 (when (viper-has-face-support-p)
847 (viper-overlay-put
848 viper-minibuffer-overlay 'face viper-minibuffer-current-face)
849 (viper-overlay-put
850 viper-minibuffer-overlay 'priority viper-minibuffer-overlay-priority)
851 ;; never detach
852 (viper-overlay-put
853 viper-minibuffer-overlay
854 (if (featurep 'emacs) 'evaporate 'detachable)
855 nil)
856 ;; make viper-minibuffer-overlay open-ended
857 ;; In emacs, it is made open ended at creation time
858 (when (featurep 'xemacs)
859 (viper-overlay-put viper-minibuffer-overlay 'start-open nil)
860 (viper-overlay-put viper-minibuffer-overlay 'end-open nil))))
862 (defun viper-check-minibuffer-overlay ()
863 (if (viper-overlay-live-p viper-minibuffer-overlay)
864 (viper-move-overlay
865 viper-minibuffer-overlay
866 (if (fboundp 'minibuffer-prompt-end) (minibuffer-prompt-end) 1)
867 (1+ (buffer-size)))
868 (setq viper-minibuffer-overlay
869 (if (featurep 'xemacs)
870 (viper-make-overlay 1 (1+ (buffer-size)) (current-buffer))
871 ;; make overlay open-ended
872 (viper-make-overlay
873 (if (fboundp 'minibuffer-prompt-end) (minibuffer-prompt-end) 1)
874 (1+ (buffer-size))
875 (current-buffer) nil 'rear-advance)))))
878 (defsubst viper-is-in-minibuffer ()
879 (save-match-data
880 (string-match "\*Minibuf-" (buffer-name))))
884 ;;; XEmacs compatibility
886 (defun viper-abbreviate-file-name (file)
887 (if (featurep 'xemacs)
888 (abbreviate-file-name file t) ; XEmacs requires addl argument
889 (abbreviate-file-name file)))
891 ;; Sit for VAL milliseconds. XEmacs doesn't support the millisecond arg
892 ;; in sit-for, so this function smoothes out the differences.
893 (defsubst viper-sit-for-short (val &optional nodisp)
894 (sit-for (/ val 1000.0) nodisp))
896 ;; EVENT may be a single event of a sequence of events
897 (defsubst viper-ESC-event-p (event)
898 (let ((ESC-keys '(?\e (control \[) escape))
899 (key (viper-event-key event)))
900 (member key ESC-keys)))
902 ;; checks if object is a marker, has a buffer, and points to within that buffer
903 (defun viper-valid-marker (marker)
904 (if (and (markerp marker) (marker-buffer marker))
905 (let ((buf (marker-buffer marker))
906 (pos (marker-position marker)))
907 (save-excursion
908 (set-buffer buf)
909 (and (<= pos (point-max)) (<= (point-min) pos))))))
911 (defsubst viper-mark-marker ()
912 (if (featurep 'xemacs) (mark-marker t)
913 (mark-marker)))
915 ;; like (set-mark-command nil) but doesn't push twice, if (car mark-ring)
916 ;; is the same as (mark t).
917 (defsubst viper-set-mark-if-necessary ()
918 (setq mark-ring (delete (viper-mark-marker) mark-ring))
919 (set-mark-command nil)
920 (setq viper-saved-mark (point)))
922 ;; In transient mark mode (zmacs mode), it is annoying when regions become
923 ;; highlighted due to Viper's pushing marks. So, we deactivate marks, unless
924 ;; the user explicitly wants highlighting, e.g., by hitting '' or ``
925 (defun viper-deactivate-mark ()
926 (if (featurep 'xemacs)
927 (zmacs-deactivate-region)
928 (deactivate-mark)))
930 (defsubst viper-leave-region-active ()
931 (if (featurep 'xemacs) (setq zmacs-region-stays t)))
933 ;; Check if arg is a valid character for register
934 ;; TYPE is a list that can contain `letter', `Letter', and `digit'.
935 ;; Letter means lowercase letters, Letter means uppercase letters, and
936 ;; digit means digits from 1 to 9.
937 ;; If TYPE is nil, then down/uppercase letters and digits are allowed.
938 (defun viper-valid-register (reg &optional type)
939 (or type (setq type '(letter Letter digit)))
940 (or (if (memq 'letter type)
941 (and (<= ?a reg) (<= reg ?z)))
942 (if (memq 'digit type)
943 (and (<= ?1 reg) (<= reg ?9)))
944 (if (memq 'Letter type)
945 (and (<= ?A reg) (<= reg ?Z)))
950 ;; it is suggested that an event must be copied before it is assigned to
951 ;; last-command-event in XEmacs
952 (defun viper-copy-event (event)
953 (if (featurep 'xemacs) (copy-event event)
954 event))
956 ;; Uses different timeouts for ESC-sequences and others
957 (defsubst viper-fast-keysequence-p ()
958 (not (viper-sit-for-short
959 (if (viper-ESC-event-p last-input-event)
960 viper-ESC-keyseq-timeout
961 viper-fast-keyseq-timeout)
962 t)))
964 ;; like read-event, but in XEmacs also try to convert to char, if possible
965 (defun viper-read-event-convert-to-char ()
966 (let (event)
967 (if (featurep 'xemacs)
968 (progn
969 (setq event (next-command-event))
970 (or (event-to-character event)
971 event))
972 (read-event))))
974 ;; Viperized read-key-sequence
975 (defun viper-read-key-sequence (prompt &optional continue-echo)
976 (let (inhibit-quit event keyseq)
977 (setq keyseq (read-key-sequence prompt continue-echo))
978 (setq event (if (featurep 'xemacs)
979 (elt keyseq 0) ; XEmacs returns vector of events
980 (elt (listify-key-sequence keyseq) 0)))
981 (if (viper-ESC-event-p event)
982 (let (unread-command-events)
983 (if (viper-fast-keysequence-p)
984 (let ((viper-vi-global-user-minor-mode nil)
985 (viper-vi-local-user-minor-mode nil)
986 (viper-vi-intercept-minor-mode nil)
987 (viper-insert-intercept-minor-mode nil)
988 (viper-replace-minor-mode nil) ; actually unnecessary
989 (viper-insert-global-user-minor-mode nil)
990 (viper-insert-local-user-minor-mode nil))
991 ;; Note: set unread-command-events only after testing for fast
992 ;; keysequence. Otherwise, viper-fast-keysequence-p will be
993 ;; always t -- whether there is anything after ESC or not
994 (viper-set-unread-command-events keyseq)
995 (setq keyseq (read-key-sequence nil)))
996 (viper-set-unread-command-events keyseq)
997 (setq keyseq (read-key-sequence nil)))))
998 keyseq))
1001 ;; This function lets function-key-map convert key sequences into logical
1002 ;; keys. This does a better job than viper-read-event when it comes to kbd
1003 ;; macros, since it enables certain macros to be shared between X and TTY modes
1004 ;; by correctly mapping key sequences for Left/Right/... (one an ascii
1005 ;; terminal) into logical keys left, right, etc.
1006 (defun viper-read-key ()
1007 (let ((overriding-local-map viper-overriding-map)
1008 (inhibit-quit t)
1009 help-char key)
1010 (use-global-map viper-overriding-map)
1011 (unwind-protect
1012 (setq key (elt (viper-read-key-sequence nil) 0))
1013 (use-global-map global-map))
1014 key))
1017 ;; Emacs has a bug in eventp, which causes (eventp nil) to return (nil)
1018 ;; instead of nil, if '(nil) was previously inadvertently assigned to
1019 ;; unread-command-events
1020 (defun viper-event-key (event)
1021 (or (and event (eventp event))
1022 (error "viper-event-key: Wrong type argument, eventp, %S" event))
1023 (when (if (featurep 'xemacs)
1024 (or (key-press-event-p event) (mouse-event-p event)) ; xemacs
1025 t ; emacs
1027 (let ((mod (event-modifiers event))
1028 basis)
1029 (setq basis
1030 (if (featurep 'xemacs)
1031 ;; XEmacs
1032 (cond ((key-press-event-p event)
1033 (event-key event))
1034 ((button-event-p event)
1035 (concat "mouse-" (prin1-to-string (event-button event))))
1037 (error "viper-event-key: Unknown event, %S" event)))
1038 ;; Emacs doesn't handle capital letters correctly, since
1039 ;; \S-a isn't considered the same as A (it behaves as
1040 ;; plain `a' instead). So we take care of this here
1041 (cond ((and (viper-characterp event) (<= ?A event) (<= event ?Z))
1042 (setq mod nil
1043 event event))
1044 ;; Emacs has the oddity whereby characters 128+char
1045 ;; represent M-char *if* this appears inside a string.
1046 ;; So, we convert them manually to (meta char).
1047 ((and (viper-characterp event)
1048 (< ?\C-? event) (<= event 255))
1049 (setq mod '(meta)
1050 event (- event ?\C-? 1)))
1051 ((and (null mod) (eq event 'return))
1052 (setq event ?\C-m))
1053 ((and (null mod) (eq event 'space))
1054 (setq event ?\ ))
1055 ((and (null mod) (eq event 'delete))
1056 (setq event ?\C-?))
1057 ((and (null mod) (eq event 'backspace))
1058 (setq event ?\C-h))
1059 (t (event-basic-type event)))
1060 ) ; (featurep 'xemacs)
1062 (if (viper-characterp basis)
1063 (setq basis
1064 (if (viper= basis ?\C-?)
1065 (list 'control '\?) ; taking care of an emacs bug
1066 (intern (char-to-string basis)))))
1067 (if mod
1068 (append mod (list basis))
1069 basis))))
1071 (defun viper-key-to-emacs-key (key)
1072 (let (key-name char-p modifiers mod-char-list base-key base-key-name)
1073 (cond ((featurep 'xemacs) key)
1075 ((symbolp key)
1076 (setq key-name (symbol-name key))
1077 (cond ((= (length key-name) 1) ; character event
1078 (string-to-char key-name))
1079 ;; Emacs doesn't recognize `return' and `escape' as events on
1080 ;; dumb terminals, so we translate them into characters
1081 ((and (featurep 'emacs) (not (viper-window-display-p))
1082 (string= key-name "return"))
1083 ?\C-m)
1084 ((and (featurep 'emacs) (not (viper-window-display-p))
1085 (string= key-name "escape"))
1086 ?\e)
1087 ;; pass symbol-event as is
1088 (t key)))
1090 ((listp key)
1091 (setq modifiers (viper-subseq key 0 (1- (length key)))
1092 base-key (viper-seq-last-elt key)
1093 base-key-name (symbol-name base-key)
1094 char-p (= (length base-key-name) 1))
1095 (setq mod-char-list
1096 (mapcar
1097 '(lambda (elt) (upcase (substring (symbol-name elt) 0 1)))
1098 modifiers))
1099 (if char-p
1100 (setq key-name
1101 (car (read-from-string
1102 (concat
1103 "?\\"
1104 (mapconcat 'identity mod-char-list "-\\")
1106 base-key-name))))
1107 (setq key-name
1108 (intern
1109 (concat
1110 (mapconcat 'identity mod-char-list "-")
1112 base-key-name))))))
1116 ;; LIS is assumed to be a list of events of characters
1117 (defun viper-eventify-list-xemacs (lis)
1118 (if (featurep 'xemacs)
1119 (mapcar
1120 (lambda (elt)
1121 (cond ((viper-characterp elt) (character-to-event elt))
1122 ((eventp elt) elt)
1123 (t (error
1124 "viper-eventify-list-xemacs: can't convert to event, %S"
1125 elt))))
1126 lis)))
1129 ;; Smoothes out the difference between Emacs' unread-command-events
1130 ;; and XEmacs unread-command-event. Arg is a character, an event, a list of
1131 ;; events or a sequence of keys.
1133 ;; Due to the way unread-command-events in Emacs (not XEmacs), a non-event
1134 ;; symbol in unread-command-events list may cause Emacs to turn this symbol
1135 ;; into an event. Below, we delete nil from event lists, since nil is the most
1136 ;; common symbol that might appear in this wrong context.
1137 (defun viper-set-unread-command-events (arg)
1138 (if (featurep 'emacs)
1139 (setq
1140 unread-command-events
1141 (let ((new-events
1142 (cond ((eventp arg) (list arg))
1143 ((listp arg) arg)
1144 ((sequencep arg)
1145 (listify-key-sequence arg))
1146 (t (error
1147 "viper-set-unread-command-events: Invalid argument, %S"
1148 arg)))))
1149 (if (not (eventp nil))
1150 (setq new-events (delq nil new-events)))
1151 (append new-events unread-command-events)))
1152 ;; XEmacs
1153 (setq
1154 unread-command-events
1155 (append
1156 (cond ((viper-characterp arg) (list (character-to-event arg)))
1157 ((eventp arg) (list arg))
1158 ((stringp arg) (mapcar 'character-to-event arg))
1159 ((vectorp arg) (append arg nil)) ; turn into list
1160 ((listp arg) (viper-eventify-list-xemacs arg))
1161 (t (error
1162 "viper-set-unread-command-events: Invalid argument, %S" arg)))
1163 unread-command-events))))
1166 ;; Check if vec is a vector of key-press events representing characters
1167 ;; XEmacs only
1168 (defun viper-event-vector-p (vec)
1169 (and (vectorp vec)
1170 (eval (cons 'and (mapcar '(lambda (elt) (if (eventp elt) t)) vec)))))
1173 ;; check if vec is a vector of character symbols
1174 (defun viper-char-symbol-sequence-p (vec)
1175 (and
1176 (sequencep vec)
1177 (eval
1178 (cons 'and
1179 (mapcar (lambda (elt)
1180 (and (symbolp elt) (= (length (symbol-name elt)) 1)))
1181 vec)))))
1184 (defun viper-char-array-p (array)
1185 (eval (cons 'and (mapcar 'viper-characterp array))))
1188 ;; Args can be a sequence of events, a string, or a Viper macro. Will try to
1189 ;; convert events to keys and, if all keys are regular printable
1190 ;; characters, will return a string. Otherwise, will return a string
1191 ;; representing a vector of converted events. If the input was a Viper macro,
1192 ;; will return a string that represents this macro as a vector.
1193 (defun viper-array-to-string (event-seq)
1194 (let (temp temp2)
1195 (cond ((stringp event-seq) event-seq)
1196 ((viper-event-vector-p event-seq)
1197 (setq temp (mapcar 'viper-event-key event-seq))
1198 (cond ((viper-char-symbol-sequence-p temp)
1199 (mapconcat 'symbol-name temp ""))
1200 ((and (viper-char-array-p
1201 (setq temp2 (mapcar 'viper-key-to-character temp))))
1202 (mapconcat 'char-to-string temp2 ""))
1203 (t (prin1-to-string (vconcat temp)))))
1204 ((viper-char-symbol-sequence-p event-seq)
1205 (mapconcat 'symbol-name event-seq ""))
1206 ((and (vectorp event-seq)
1207 (viper-char-array-p
1208 (setq temp (mapcar 'viper-key-to-character event-seq))))
1209 (mapconcat 'char-to-string temp ""))
1210 (t (prin1-to-string event-seq)))))
1212 (defun viper-key-press-events-to-chars (events)
1213 (mapconcat (if (featurep 'xemacs)
1214 (lambda (elt) (char-to-string (event-to-character elt))) ; xemacs
1215 'char-to-string ; emacs
1217 events
1218 ""))
1221 (defun viper-read-char-exclusive ()
1222 (let (char
1223 (echo-keystrokes 1))
1224 (while (null char)
1225 (condition-case nil
1226 (setq char (read-char))
1227 (error
1228 ;; skip event if not char
1229 (viper-read-event))))
1230 char))
1232 ;; key is supposed to be in viper's representation, e.g., (control l), a
1233 ;; character, etc.
1234 (defun viper-key-to-character (key)
1235 (cond ((eq key 'space) ?\ )
1236 ((eq key 'delete) ?\C-?)
1237 ((eq key 'return) ?\C-m)
1238 ((eq key 'backspace) ?\C-h)
1239 ((and (symbolp key)
1240 (= 1 (length (symbol-name key))))
1241 (string-to-char (symbol-name key)))
1242 ((and (listp key)
1243 (eq (car key) 'control)
1244 (symbol-name (nth 1 key))
1245 (= 1 (length (symbol-name (nth 1 key)))))
1246 (read (format "?\\C-%s" (symbol-name (nth 1 key)))))
1247 (t key)))
1250 (defun viper-setup-master-buffer (&rest other-files-or-buffers)
1251 "Set up the current buffer as a master buffer.
1252 Arguments become related buffers. This function should normally be used in
1253 the `Local variables' section of a file."
1254 (setq viper-related-files-and-buffers-ring
1255 (make-ring (1+ (length other-files-or-buffers))))
1256 (mapc '(lambda (elt)
1257 (viper-ring-insert viper-related-files-and-buffers-ring elt))
1258 other-files-or-buffers)
1259 (viper-ring-insert viper-related-files-and-buffers-ring (buffer-name))
1262 ;;; Movement utilities
1264 ;; Characters that should not be considered as part of the word, in reformed-vi
1265 ;; syntax mode.
1266 ;; Note: \\ (quoted \) must appear before `-' because this string is listified
1267 ;; into characters at some point and then put back to string. The result is
1268 ;; used in skip-chars-forward, which treats - specially. Here we achieve the
1269 ;; effect of quoting - and preventing it from being special.
1270 (defconst viper-non-word-characters-reformed-vi
1271 "!@#$%^&*()\\-+=|\\~`{}[];:'\",<.>/?")
1272 ;; These are characters that are not to be considered as parts of a word in
1273 ;; Viper.
1274 ;; Set each time state changes and at loading time
1275 (viper-deflocalvar viper-non-word-characters nil)
1277 ;; must be buffer-local
1278 (viper-deflocalvar viper-ALPHA-char-class "w"
1279 "String of syntax classes characterizing Viper's alphanumeric symbols.
1280 In addition, the symbol `_' may be considered alphanumeric if
1281 `viper-syntax-preference' is `strict-vi' or `reformed-vi'.")
1283 (defconst viper-strict-ALPHA-chars "a-zA-Z0-9_"
1284 "Regexp matching the set of alphanumeric characters acceptable to strict
1285 Vi.")
1286 (defconst viper-strict-SEP-chars " \t\n"
1287 "Regexp matching the set of alphanumeric characters acceptable to strict
1288 Vi.")
1289 (defconst viper-strict-SEP-chars-sans-newline " \t"
1290 "Regexp matching the set of alphanumeric characters acceptable to strict
1291 Vi.")
1293 (defconst viper-SEP-char-class " -"
1294 "String of syntax classes for Vi separators.
1295 Usually contains ` ', linefeed, TAB or formfeed.")
1298 ;; Set Viper syntax classes and related variables according to
1299 ;; `viper-syntax-preference'.
1300 (defun viper-update-syntax-classes (&optional set-default)
1301 (let ((preference (cond ((eq viper-syntax-preference 'emacs)
1302 "w") ; Viper words have only Emacs word chars
1303 ((eq viper-syntax-preference 'extended)
1304 "w_") ; Viper words have Emacs word & symbol chars
1305 (t "w"))) ; Viper words are Emacs words plus `_'
1306 (non-word-chars (cond ((eq viper-syntax-preference 'reformed-vi)
1307 (viper-string-to-list
1308 viper-non-word-characters-reformed-vi))
1309 (t nil))))
1310 (if set-default
1311 (setq-default viper-ALPHA-char-class preference
1312 viper-non-word-characters non-word-chars)
1313 (setq viper-ALPHA-char-class preference
1314 viper-non-word-characters non-word-chars))
1317 ;; SYMBOL is used because customize requires it, but it is ignored, unless it
1318 ;; is `nil'. If nil, use setq.
1319 (defun viper-set-syntax-preference (&optional symbol value)
1320 "Set Viper syntax preference.
1321 If called interactively or if SYMBOL is nil, sets syntax preference in current
1322 buffer. If called non-interactively, preferably via the customization widget,
1323 sets the default value."
1324 (interactive)
1325 (or value
1326 (setq value
1327 (completing-read
1328 "Viper syntax preference: "
1329 '(("strict-vi") ("reformed-vi") ("extended") ("emacs"))
1330 nil 'require-match)))
1331 (if (stringp value) (setq value (intern value)))
1332 (or (memq value '(strict-vi reformed-vi extended emacs))
1333 (error "Invalid Viper syntax preference, %S" value))
1334 (if symbol
1335 (setq-default viper-syntax-preference value)
1336 (setq viper-syntax-preference value))
1337 (viper-update-syntax-classes))
1339 (defcustom viper-syntax-preference 'reformed-vi
1340 "*Syntax type characterizing Viper's alphanumeric symbols.
1341 Affects movement and change commands that deal with Vi-style words.
1342 Works best when set in the hooks to various major modes.
1344 `strict-vi' means Viper words are (hopefully) exactly as in Vi.
1346 `reformed-vi' means Viper words are like Emacs words \(as determined using
1347 Emacs syntax tables, which are different for different major modes\) with two
1348 exceptions: the symbol `_' is always part of a word and typical Vi non-word
1349 symbols, such as `,',:,\",),{, etc., are excluded.
1350 This behaves very close to `strict-vi', but also works well with non-ASCII
1351 characters from various alphabets.
1353 `extended' means Viper word constituents are symbols that are marked as being
1354 parts of words OR symbols in Emacs syntax tables.
1355 This is most appropriate for major modes intended for editing programs.
1357 `emacs' means Viper words are the same as Emacs words as specified by Emacs
1358 syntax tables.
1359 This option is appropriate if you like Emacs-style words."
1360 :type '(radio (const strict-vi) (const reformed-vi)
1361 (const extended) (const emacs))
1362 :set 'viper-set-syntax-preference
1363 :group 'viper)
1364 (make-variable-buffer-local 'viper-syntax-preference)
1367 ;; addl-chars are characters to be temporarily considered as alphanumerical
1368 (defun viper-looking-at-alpha (&optional addl-chars)
1369 (or (stringp addl-chars) (setq addl-chars ""))
1370 (if (eq viper-syntax-preference 'reformed-vi)
1371 (setq addl-chars (concat addl-chars "_")))
1372 (let ((char (char-after (point))))
1373 (if char
1374 (if (eq viper-syntax-preference 'strict-vi)
1375 (looking-at (concat "[" viper-strict-ALPHA-chars addl-chars "]"))
1377 ;; or one of the additional chars being asked to include
1378 (viper-memq-char char (viper-string-to-list addl-chars))
1379 (and
1380 ;; not one of the excluded word chars (note:
1381 ;; viper-non-word-characters is a list)
1382 (not (viper-memq-char char viper-non-word-characters))
1383 ;; char of the Viper-word syntax class
1384 (viper-memq-char (char-syntax char)
1385 (viper-string-to-list viper-ALPHA-char-class))))))
1388 (defun viper-looking-at-separator ()
1389 (let ((char (char-after (point))))
1390 (if char
1391 (if (eq viper-syntax-preference 'strict-vi)
1392 (viper-memq-char char (viper-string-to-list viper-strict-SEP-chars))
1393 (or (eq char ?\n) ; RET is always a separator in Vi
1394 (viper-memq-char (char-syntax char)
1395 (viper-string-to-list viper-SEP-char-class)))))
1398 (defsubst viper-looking-at-alphasep (&optional addl-chars)
1399 (or (viper-looking-at-separator) (viper-looking-at-alpha addl-chars)))
1401 (defun viper-skip-alpha-forward (&optional addl-chars)
1402 (or (stringp addl-chars) (setq addl-chars ""))
1403 (viper-skip-syntax
1404 'forward
1405 (cond ((eq viper-syntax-preference 'strict-vi)
1407 (t viper-ALPHA-char-class))
1408 (cond ((eq viper-syntax-preference 'strict-vi)
1409 (concat viper-strict-ALPHA-chars addl-chars))
1410 (t addl-chars))))
1412 (defun viper-skip-alpha-backward (&optional addl-chars)
1413 (or (stringp addl-chars) (setq addl-chars ""))
1414 (viper-skip-syntax
1415 'backward
1416 (cond ((eq viper-syntax-preference 'strict-vi)
1418 (t viper-ALPHA-char-class))
1419 (cond ((eq viper-syntax-preference 'strict-vi)
1420 (concat viper-strict-ALPHA-chars addl-chars))
1421 (t addl-chars))))
1423 ;; weird syntax tables may confuse strict-vi style
1424 (defsubst viper-skip-all-separators-forward (&optional within-line)
1425 (if (eq viper-syntax-preference 'strict-vi)
1426 (if within-line
1427 (skip-chars-forward viper-strict-SEP-chars-sans-newline)
1428 (skip-chars-forward viper-strict-SEP-chars))
1429 (viper-skip-syntax 'forward
1430 viper-SEP-char-class
1431 (or within-line "\n")
1432 (if within-line (viper-line-pos 'end)))))
1434 (defsubst viper-skip-all-separators-backward (&optional within-line)
1435 (if (eq viper-syntax-preference 'strict-vi)
1436 (if within-line
1437 (skip-chars-backward viper-strict-SEP-chars-sans-newline)
1438 (skip-chars-backward viper-strict-SEP-chars))
1439 (viper-skip-syntax 'backward
1440 viper-SEP-char-class
1441 (or within-line "\n")
1442 (if within-line (viper-line-pos 'start)))))
1443 (defun viper-skip-nonseparators (direction)
1444 (viper-skip-syntax
1445 direction
1446 (concat "^" viper-SEP-char-class)
1448 (viper-line-pos (if (eq direction 'forward) 'end 'start))))
1451 ;; skip over non-word constituents and non-separators
1452 (defun viper-skip-nonalphasep-forward ()
1453 (if (eq viper-syntax-preference 'strict-vi)
1454 (skip-chars-forward
1455 (concat "^" viper-strict-SEP-chars viper-strict-ALPHA-chars))
1456 (viper-skip-syntax
1457 'forward
1458 (concat "^" viper-ALPHA-char-class viper-SEP-char-class)
1459 ;; Emacs may consider some of these as words, but we don't want them
1460 viper-non-word-characters
1461 (viper-line-pos 'end))))
1463 (defun viper-skip-nonalphasep-backward ()
1464 (if (eq viper-syntax-preference 'strict-vi)
1465 (skip-chars-backward
1466 (concat "^" viper-strict-SEP-chars viper-strict-ALPHA-chars))
1467 (viper-skip-syntax
1468 'backward
1469 (concat "^" viper-ALPHA-char-class viper-SEP-char-class)
1470 ;; Emacs may consider some of these as words, but we don't want them
1471 viper-non-word-characters
1472 (viper-line-pos 'start))))
1474 ;; Skip SYNTAX like skip-syntax-* and ADDL-CHARS like skip-chars-*
1475 ;; Return the number of chars traveled.
1476 ;; Both SYNTAX or ADDL-CHARS can be strings or lists of characters.
1477 ;; When SYNTAX is "w", then viper-non-word-characters are not considered to be
1478 ;; words, even if Emacs syntax table says they are.
1479 (defun viper-skip-syntax (direction syntax addl-chars &optional limit)
1480 (let ((total 0)
1481 (local 1)
1482 (skip-chars-func
1483 (if (eq direction 'forward)
1484 'skip-chars-forward 'skip-chars-backward))
1485 (skip-syntax-func
1486 (if (eq direction 'forward)
1487 'viper-forward-char-carefully 'viper-backward-char-carefully))
1488 char-looked-at syntax-of-char-looked-at negated-syntax)
1489 (setq addl-chars
1490 (cond ((listp addl-chars) (viper-charlist-to-string addl-chars))
1491 ((stringp addl-chars) addl-chars)
1492 (t "")))
1493 (setq syntax
1494 (cond ((listp syntax) syntax)
1495 ((stringp syntax) (viper-string-to-list syntax))
1496 (t nil)))
1497 (if (memq ?^ syntax) (setq negated-syntax t))
1499 (while (and (not (= local 0))
1500 (cond ((eq direction 'forward)
1501 (not (eobp)))
1502 (t (not (bobp)))))
1503 (setq char-looked-at (viper-char-at-pos direction)
1504 ;; if outside the range, set to nil
1505 syntax-of-char-looked-at (if char-looked-at
1506 (char-syntax char-looked-at)))
1507 (setq local
1508 (+ (if (and
1509 (cond ((and limit (eq direction 'forward))
1510 (< (point) limit))
1511 (limit ; backward & limit
1512 (> (point) limit))
1513 (t t)) ; no limit
1514 ;; char under/before cursor has appropriate syntax
1515 (if negated-syntax
1516 (not (memq syntax-of-char-looked-at syntax))
1517 (memq syntax-of-char-looked-at syntax))
1518 ;; if char-syntax class is "word", make sure it is not one
1519 ;; of the excluded characters
1520 (if (and (eq syntax-of-char-looked-at ?w)
1521 (not negated-syntax))
1522 (not (viper-memq-char
1523 char-looked-at viper-non-word-characters))
1525 (funcall skip-syntax-func 1)
1527 (funcall skip-chars-func addl-chars limit)))
1528 (setq total (+ total local)))
1529 total
1532 ;; tells when point is at the beginning of field
1533 (defun viper-beginning-of-field ()
1534 (or (bobp)
1535 (not (eq (get-char-property (point) 'field)
1536 (get-char-property (1- (point)) 'field)))))
1539 ;; this is copied from cl-extra.el
1540 ;; Return the subsequence of SEQ from START to END.
1541 ;; If END is omitted, it defaults to the length of the sequence.
1542 ;; If START or END is negative, it counts from the end.
1543 (defun viper-subseq (seq start &optional end)
1544 (if (stringp seq) (substring seq start end)
1545 (let (len)
1546 (and end (< end 0) (setq end (+ end (setq len (length seq)))))
1547 (if (< start 0) (setq start (+ start (or len (setq len (length seq))))))
1548 (cond ((listp seq)
1549 (if (> start 0) (setq seq (nthcdr start seq)))
1550 (if end
1551 (let ((res nil))
1552 (while (>= (setq end (1- end)) start)
1553 (push (pop seq) res))
1554 (nreverse res))
1555 (copy-sequence seq)))
1557 (or end (setq end (or len (length seq))))
1558 (let ((res (make-vector (max (- end start) 0) nil))
1559 (i 0))
1560 (while (< start end)
1561 (aset res i (aref seq start))
1562 (setq i (1+ i) start (1+ start)))
1563 res))))))
1567 (provide 'viper-util)
1570 ;;; Local Variables:
1571 ;;; eval: (put 'viper-deflocalvar 'lisp-indent-hook 'defun)
1572 ;;; End:
1574 ;;; arch-tag: 7f023fd5-dd9e-4378-a397-9c179553b0e3
1575 ;;; viper-util.el ends here