(cvs-string-prefix-p): Use compare-strings.
[bpt/emacs.git] / lisp / pcvs-util.el
CommitLineData
3afbc435 1;;; pcvs-util.el --- utility functions for PCL-CVS -*- byte-compile-dynamic: t -*-
5b467bf4 2
ac62b9e4
SM
3;; Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
4;; 2000, 2001, 2004 Free Software Foundation, Inc.
5b467bf4
SM
5
6;; Author: Stefan Monnier <monnier@cs.yale.edu>
7;; Keywords: pcl-cvs
5b467bf4
SM
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
25
26;;; Commentary:
27
28
29;;; Code:
30
31(eval-when-compile (require 'cl))
32
33;;;;
34;;;; list processing
80786c0a 35;;;;
5b467bf4
SM
36
37(defsubst cvs-car (x) (if (consp x) (car x) x))
38(defalias 'cvs-cdr 'cdr-safe)
39(defsubst cvs-append (&rest xs)
40 (apply 'append (mapcar (lambda (x) (if (listp x) x (list x))) xs)))
41
42(defsubst cvs-every (-cvs-every-f -cvs-every-l)
43 (while (consp -cvs-every-l)
44 (unless (funcall -cvs-every-f (pop -cvs-every-l))
45 (setq -cvs-every-l t)))
46 (not -cvs-every-l))
47
48(defun cvs-union (xs ys)
49 (let ((zs ys))
50 (dolist (x xs zs)
51 (unless (member x ys) (push x zs)))))
f1180544 52
5b467bf4
SM
53(defun cvs-map (-cvs-map-f &rest -cvs-map-ls)
54 (unless (cvs-every 'null -cvs-map-ls)
55 (cons (apply -cvs-map-f (mapcar 'car -cvs-map-ls))
56 (apply 'cvs-map -cvs-map-f (mapcar 'cdr -cvs-map-ls)))))
57
58(defun cvs-first (l &optional n)
59 (if (null n) (car l)
60 (when l
61 (let* ((nl (list (pop l)))
62 (ret nl))
63 (while (and l (> n 1))
64 (setcdr nl (list (pop l)))
65 (setq nl (cdr nl))
66 (decf n))
67 ret))))
68
69(defun cvs-partition (p l)
70 "Partition a list L into two lists based on predicate P.
71The function returns a `cons' cell where the `car' contains
72elements of L for which P is true while the `cdr' contains
73the other elements. The ordering among elements is maintained."
74 (let (car cdr)
75 (dolist (x l)
76 (if (funcall p x) (push x car) (push x cdr)))
77 (cons (nreverse car) (nreverse cdr))))
78
178917f7
SM
79;;;
80;;; frame, window, buffer handling
81;;;
5b467bf4
SM
82
83(defun cvs-pop-to-buffer-same-frame (buf)
84 "Pop to BUF like `pop-to-buffer' but staying on the same frame.
85If `pop-to-buffer' would have opened a new frame, this function would
178917f7 86try to split a new window instead."
5b467bf4
SM
87 (let ((pop-up-windows (or pop-up-windows pop-up-frames))
88 (pop-up-frames nil))
89 (or (let ((buf (get-buffer-window buf))) (and buf (select-window buf)))
90 (and pop-up-windows
91 (ignore-errors (select-window (split-window-vertically)))
92 (switch-to-buffer buf))
93 (pop-to-buffer (current-buffer)))))
94
95(defun cvs-bury-buffer (buf &optional mainbuf)
96 "Hide the buffer BUF that was temporarily popped up.
97BUF is assumed to be a temporary buffer used from the buffer MAINBUF."
98 (interactive (list (current-buffer)))
99 (save-current-buffer
100 (let ((win (if (eq buf (window-buffer (selected-window))) (selected-window)
101 (get-buffer-window buf t))))
102 (when win
103 (if (window-dedicated-p win)
104 (condition-case ()
105 (delete-window win)
106 (error (iconify-frame (window-frame win))))
107 (if (and mainbuf (get-buffer-window mainbuf))
84dd85f5
SM
108 ;; FIXME: if the buffer popped into a pre-existing window,
109 ;; we don't want to delete that window.
341c19b9 110 t ;;(delete-window win)
84dd85f5 111 ))))
5b467bf4
SM
112 (with-current-buffer buf
113 (bury-buffer (unless (and (eq buf (window-buffer (selected-window)))
114 (not (window-dedicated-p (selected-window))))
115 buf)))
116 (when mainbuf
117 (let ((mainwin (or (get-buffer-window mainbuf)
118 (get-buffer-window mainbuf 'visible))))
119 (when mainwin (select-window mainwin))))))
f1180544 120
5b467bf4
SM
121(defun cvs-get-buffer-create (name &optional noreuse)
122 "Create a buffer NAME unless such a buffer already exists.
123If the NAME looks like an absolute file name, the buffer will be created
124with `create-file-buffer' and will probably get another name than NAME.
125In such a case, the search for another buffer with the same name doesn't
126use the buffer name but the buffer's `list-buffers-directory' variable.
127If NOREUSE is non-nil, always return a new buffer."
128 (or (and (not (file-name-absolute-p name)) (get-buffer-create name))
129 (unless noreuse
130 (dolist (buf (buffer-list))
131 (with-current-buffer buf
132 (when (equal name list-buffers-directory)
133 (return buf)))))
134 (with-current-buffer (create-file-buffer name)
135 (set (make-local-variable 'list-buffers-directory) name)
136 (current-buffer))))
137
138;;;;
139;;;; string processing
140;;;;
141
80786c0a
SM
142(defun cvs-insert-strings (strings)
143 "Insert a list of STRINGS into the current buffer.
144Uses columns to keep the listing readable but compact."
145 (when (consp strings)
146 (let* ((length (apply 'max (mapcar 'length strings)))
147 (wwidth (1- (window-width)))
148 (columns (min
149 ;; At least 2 columns; at least 2 spaces between columns.
150 (max 2 (/ wwidth (+ 2 length)))
151 ;; Don't allocate more columns than we can fill.
0c8f5edd 152 ;; Windows can't show less than 3 lines anyway.
80786c0a
SM
153 (max 1 (/ (length strings) 2))))
154 (colwidth (/ wwidth columns)))
0c8f5edd 155 ;; Use tab-width rather than indent-to.
80786c0a
SM
156 (setq tab-width colwidth)
157 ;; The insertion should be "sensible" no matter what choices were made.
158 (dolist (str strings)
159 (unless (bolp) (insert " \t"))
160 (when (< wwidth (+ (max colwidth (length str)) (current-column)))
161 (delete-char -2) (insert "\n"))
162 (insert str)))))
163
164
5b467bf4
SM
165(defun cvs-file-to-string (file &optional oneline args)
166 "Read the content of FILE and return it as a string.
167If ONELINE is t, only the first line (no \\n) will be returned.
168If ARGS is non-nil, the file will be executed with ARGS as its
169arguments. If ARGS is not a list, no argument will be passed."
6d57b1a3
SM
170 (condition-case nil
171 (with-temp-buffer
172 (if args
173 (apply 'call-process
174 file nil t nil (when (listp args) args))
175 (insert-file-contents file))
176 (goto-char (point-min))
177 (buffer-substring (point)
178 (if oneline (line-end-position) (point-max))))
179 (file-error nil)))
5b467bf4
SM
180
181(defun cvs-string-prefix-p (str1 str2)
182 "Tell whether STR1 is a prefix of STR2."
ac62b9e4 183 (eq t (compare-strings str2 nil (length str1) str1 nil nil)))
5b467bf4
SM
184
185;; (string->strings (strings->string X)) == X
186(defun cvs-strings->string (strings &optional separator)
187 "Concatenate the STRINGS, adding the SEPARATOR (default \" \").
188This tries to quote the strings to avoid ambiguity such that
189 (cvs-string->strings (cvs-strings->string strs)) == strs
190Only some SEPARATOR will work properly."
191 (let ((sep (or separator " ")))
192 (mapconcat
193 (lambda (str)
194 (if (string-match "[\\\"]" str)
b42f693c 195 (concat "\"" (replace-regexp-in-string "[\\\"]" "\\\\\\&" str) "\"")
5b467bf4
SM
196 str))
197 strings sep)))
198
199;; (string->strings (strings->string X)) == X
200(defun cvs-string->strings (string &optional separator)
201 "Split the STRING into a list of strings.
202It understands elisp style quoting within STRING such that
203 (cvs-string->strings (cvs-strings->string strs)) == strs
204The SEPARATOR regexp defaults to \"\\s-+\"."
205 (let ((sep (or separator "\\s-+"))
206 (i (string-match "[\"]" string)))
207 (if (null i) (split-string string sep) ; no quoting: easy
208 (append (unless (eq i 0) (split-string (substring string 0 i) sep))
209 (let ((rfs (read-from-string string i)))
210 (cons (car rfs)
6d57b1a3
SM
211 (cvs-string->strings (substring string (cdr rfs))
212 sep)))))))
5b467bf4 213
f1180544 214;;;;
5b467bf4 215;;;; file names
f1180544 216;;;;
5b467bf4
SM
217
218(defsubst cvs-expand-dir-name (d)
219 (file-name-as-directory (expand-file-name d)))
220
221;;;;
222;;;; (interactive <foo>) support function
223;;;;
224
225(defstruct (cvs-qtypedesc
226 (:constructor nil) (:copier nil)
227 (:constructor cvs-qtypedesc-create
228 (str2obj obj2str &optional complete hist-sym require)))
229 str2obj
230 obj2str
231 hist-sym
232 complete
233 require)
234
235
236(defconst cvs-qtypedesc-string1 (cvs-qtypedesc-create 'identity 'identity t))
237(defconst cvs-qtypedesc-string (cvs-qtypedesc-create 'identity 'identity))
238(defconst cvs-qtypedesc-strings
239 (cvs-qtypedesc-create 'cvs-string->strings 'cvs-strings->string nil))
240
241(defun cvs-query-read (default prompt qtypedesc &optional hist-sym)
242 (let* ((qtypedesc (or qtypedesc cvs-qtypedesc-strings))
243 (hist-sym (or hist-sym (cvs-qtypedesc-hist-sym qtypedesc)))
244 (complete (cvs-qtypedesc-complete qtypedesc))
245 (completions (and (functionp complete) (funcall complete)))
246 (initval (funcall (cvs-qtypedesc-obj2str qtypedesc) default)))
247 (funcall (cvs-qtypedesc-str2obj qtypedesc)
248 (cond
249 ((null complete) (read-string prompt initval hist-sym))
250 ((functionp complete)
251 (completing-read prompt completions
252 nil (cvs-qtypedesc-require qtypedesc)
253 initval hist-sym))
254 (t initval)))))
255
f1180544 256;;;;
5b467bf4 257;;;; Flags handling
f1180544 258;;;;
5b467bf4
SM
259
260(defstruct (cvs-flags
261 (:constructor nil)
262 (:constructor -cvs-flags-make
263 (desc defaults &optional qtypedesc hist-sym)))
264 defaults persist desc qtypedesc hist-sym)
265
266(defmacro cvs-flags-define (sym defaults
267 &optional desc qtypedesc hist-sym docstring)
268 `(defconst ,sym
269 (let ((bound (boundp ',sym)))
270 (if (and bound (cvs-flags-p ,sym)) ,sym
271 (let ((defaults ,defaults))
272 (-cvs-flags-make ,desc
273 (if bound (cons ,sym (cdr defaults)) defaults)
274 ,qtypedesc ,hist-sym))))
275 ,docstring))
276
277(defun cvs-flags-query (sym &optional desc arg)
278 "Query flags based on SYM.
279Optional argument DESC will be used for the prompt
280If ARG (or a prefix argument) is nil, just use the 0th default.
281If it is a non-negative integer, use the corresponding default.
282If it is a negative integer query for a new value of the corresponding
283 default and return that new value.
284If it is \\[universal-argument], just query and return a value without
285 altering the defaults.
286If it is \\[universal-argument] \\[universal-argument], behave just
287 as if a negative zero was provided."
288 (let* ((flags (symbol-value sym))
289 (desc (or desc (cvs-flags-desc flags)))
290 (qtypedesc (cvs-flags-qtypedesc flags))
291 (hist-sym (cvs-flags-hist-sym flags))
292 (arg (if (eq arg 'noquery) 0 (or arg current-prefix-arg 0)))
293 (numarg (prefix-numeric-value arg))
294 (defaults (cvs-flags-defaults flags))
295 (permstr (if (< numarg 0) (format " (%sth default)" (- numarg)))))
296 ;; special case for universal-argument
297 (when (consp arg)
298 (setq permstr (if (> numarg 4) " (permanent)" ""))
299 (setq numarg 0))
300
301 ;; sanity check
302 (unless (< (abs numarg) (length defaults))
3afbc435 303 (error "There is no %sth default" (abs numarg)))
5b467bf4
SM
304
305 (if permstr
306 (let* ((prompt (format "%s%s: " desc permstr))
307 (fs (cvs-query-read (nth (- numarg) (cvs-flags-defaults flags))
308 prompt qtypedesc hist-sym)))
309 (when (not (equal permstr ""))
310 (setf (nth (- numarg) (cvs-flags-defaults flags)) fs))
311 fs)
312 (nth numarg defaults))))
313
314(defsubst cvs-flags-set (sym index value)
315 "Set SYM's INDEX'th setting to VALUE."
316 (setf (nth index (cvs-flags-defaults (symbol-value sym))) value))
317
f1180544 318;;;;
5b467bf4 319;;;; Prefix keys
f1180544 320;;;;
5b467bf4
SM
321
322(defconst cvs-prefix-number 10)
323
324(defsubst cvs-prefix-sym (sym) (intern (concat (symbol-name sym) "-cps")))
325
326(defmacro cvs-prefix-define (sym docstring desc defaults
327 &optional qtypedesc hist-sym)
328 (let ((cps (cvs-prefix-sym sym)))
329 `(progn
630784a2 330 (defvar ,sym nil ,(concat (or docstring "") "
5b467bf4 331See `cvs-prefix-set' for further description of the behavior."))
08171162 332 (defvar ,cps
5b467bf4
SM
333 (let ((defaults ,defaults))
334 ;; sanity ensurance
335 (unless (>= (length defaults) cvs-prefix-number)
336 (setq defaults (append defaults
337 (make-list (1- cvs-prefix-number)
dedffa6a 338 (nth 0 defaults)))))
5b467bf4
SM
339 (-cvs-flags-make ,desc defaults ,qtypedesc ,hist-sym))))))
340
341(defun cvs-prefix-make-local (sym)
342 (let ((cps (cvs-prefix-sym sym)))
343 (make-local-variable sym)
344 (set (make-local-variable cps) (copy-cvs-flags (symbol-value cps)))))
345
346(defun cvs-prefix-set (sym arg)
347 ;; we could distinguish between numeric and non-numeric prefix args instead of
348 ;; relying on that magic `4'.
349 "Set the cvs-prefix contained in SYM.
350If ARG is between 0 and 9, it selects the corresponding default.
351If ARG is negative (or \\[universal-argument] which corresponds to negative 0),
352 it queries the user and sets the -ARG'th default.
353If ARG is greater than 9 (or \\[universal-argument] \\[universal-argument]),
354 the (ARG mod 10)'th prefix is made persistent.
0ff9b955 355If ARG is nil toggle the PREFIX's value between its 0th default and nil
5b467bf4
SM
356 and reset the persistence."
357 (let* ((prefix (symbol-value (cvs-prefix-sym sym)))
358 (numarg (if (integerp arg) arg 0))
359 (defs (cvs-flags-defaults prefix)))
360
361 ;; set persistence if requested
362 (when (> (prefix-numeric-value arg) 9)
363 (setf (cvs-flags-persist prefix) t)
364 (setq numarg (mod numarg 10)))
365
366 ;; set the value
367 (set sym
368 (cond
369 ((null arg)
370 (setf (cvs-flags-persist prefix) nil)
dedffa6a 371 (unless (symbol-value sym) (nth 0 (cvs-flags-defaults prefix))))
5b467bf4
SM
372
373 ((or (consp arg) (< numarg 0))
374 (setf (nth (- numarg) (cvs-flags-defaults prefix))
375 (cvs-query-read (nth (- numarg) (cvs-flags-defaults prefix))
376 (format "%s: " (cvs-flags-desc prefix))
377 (cvs-flags-qtypedesc prefix)
378 (cvs-flags-hist-sym prefix))))
379 (t (nth numarg (cvs-flags-defaults prefix)))))
380 (force-mode-line-update)))
381
382(defun cvs-prefix-get (sym &optional read-only)
383 "Return the current value of the prefix SYM.
384and reset it unless READ-ONLY is non-nil."
385 (prog1 (symbol-value sym)
386 (unless (or read-only
387 (cvs-flags-persist (symbol-value (cvs-prefix-sym sym))))
388 (set sym nil)
389 (force-mode-line-update))))
390
391(provide 'pcvs-util)
392
ac62b9e4 393;; arch-tag: 3b2588bb-2ae3-4f1f-bf5b-dea91b1f8a59
3afbc435 394;;; pcvs-util.el ends here