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