Update years in copyright notice; nfc.
[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 3;; Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
aaef169d 4;; 2000, 2001, 2002, 2003, 2004, 2005, 2006 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
086add15
LK
23;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24;; Boston, MA 02110-1301, USA.
5b467bf4
SM
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))))
931a3331
RS
107;;; (if (and mainbuf (get-buffer-window mainbuf))
108;;; ;; FIXME: if the buffer popped into a pre-existing window,
109;;; ;; we don't want to delete that window.
110;;; t ;;(delete-window win)
111;;; )
112 )))
5b467bf4
SM
113 (with-current-buffer buf
114 (bury-buffer (unless (and (eq buf (window-buffer (selected-window)))
115 (not (window-dedicated-p (selected-window))))
116 buf)))
117 (when mainbuf
118 (let ((mainwin (or (get-buffer-window mainbuf)
119 (get-buffer-window mainbuf 'visible))))
120 (when mainwin (select-window mainwin))))))
f1180544 121
5b467bf4
SM
122(defun cvs-get-buffer-create (name &optional noreuse)
123 "Create a buffer NAME unless such a buffer already exists.
124If the NAME looks like an absolute file name, the buffer will be created
125with `create-file-buffer' and will probably get another name than NAME.
126In such a case, the search for another buffer with the same name doesn't
127use the buffer name but the buffer's `list-buffers-directory' variable.
128If NOREUSE is non-nil, always return a new buffer."
129 (or (and (not (file-name-absolute-p name)) (get-buffer-create name))
130 (unless noreuse
131 (dolist (buf (buffer-list))
132 (with-current-buffer buf
133 (when (equal name list-buffers-directory)
134 (return buf)))))
135 (with-current-buffer (create-file-buffer name)
136 (set (make-local-variable 'list-buffers-directory) name)
137 (current-buffer))))
138
139;;;;
140;;;; string processing
141;;;;
142
80786c0a
SM
143(defun cvs-insert-strings (strings)
144 "Insert a list of STRINGS into the current buffer.
145Uses columns to keep the listing readable but compact."
146 (when (consp strings)
147 (let* ((length (apply 'max (mapcar 'length strings)))
148 (wwidth (1- (window-width)))
149 (columns (min
150 ;; At least 2 columns; at least 2 spaces between columns.
151 (max 2 (/ wwidth (+ 2 length)))
152 ;; Don't allocate more columns than we can fill.
0c8f5edd 153 ;; Windows can't show less than 3 lines anyway.
80786c0a
SM
154 (max 1 (/ (length strings) 2))))
155 (colwidth (/ wwidth columns)))
0c8f5edd 156 ;; Use tab-width rather than indent-to.
80786c0a
SM
157 (setq tab-width colwidth)
158 ;; The insertion should be "sensible" no matter what choices were made.
159 (dolist (str strings)
160 (unless (bolp) (insert " \t"))
161 (when (< wwidth (+ (max colwidth (length str)) (current-column)))
162 (delete-char -2) (insert "\n"))
163 (insert str)))))
164
165
5b467bf4
SM
166(defun cvs-file-to-string (file &optional oneline args)
167 "Read the content of FILE and return it as a string.
168If ONELINE is t, only the first line (no \\n) will be returned.
169If ARGS is non-nil, the file will be executed with ARGS as its
170arguments. If ARGS is not a list, no argument will be passed."
6d57b1a3
SM
171 (condition-case nil
172 (with-temp-buffer
173 (if args
174 (apply 'call-process
175 file nil t nil (when (listp args) args))
176 (insert-file-contents file))
177 (goto-char (point-min))
178 (buffer-substring (point)
179 (if oneline (line-end-position) (point-max))))
180 (file-error nil)))
5b467bf4
SM
181
182(defun cvs-string-prefix-p (str1 str2)
183 "Tell whether STR1 is a prefix of STR2."
ac62b9e4 184 (eq t (compare-strings str2 nil (length str1) str1 nil nil)))
5b467bf4
SM
185
186;; (string->strings (strings->string X)) == X
187(defun cvs-strings->string (strings &optional separator)
188 "Concatenate the STRINGS, adding the SEPARATOR (default \" \").
189This tries to quote the strings to avoid ambiguity such that
190 (cvs-string->strings (cvs-strings->string strs)) == strs
e05a39ba 191Only some SEPARATORs will work properly."
5b467bf4
SM
192 (let ((sep (or separator " ")))
193 (mapconcat
194 (lambda (str)
195 (if (string-match "[\\\"]" str)
b42f693c 196 (concat "\"" (replace-regexp-in-string "[\\\"]" "\\\\\\&" str) "\"")
5b467bf4
SM
197 str))
198 strings sep)))
199
200;; (string->strings (strings->string X)) == X
201(defun cvs-string->strings (string &optional separator)
202 "Split the STRING into a list of strings.
203It understands elisp style quoting within STRING such that
204 (cvs-string->strings (cvs-strings->string strs)) == strs
205The SEPARATOR regexp defaults to \"\\s-+\"."
206 (let ((sep (or separator "\\s-+"))
207 (i (string-match "[\"]" string)))
b70d9316
SM
208 (if (null i) (split-string string sep t) ; no quoting: easy
209 (append (unless (eq i 0) (split-string (substring string 0 i) sep t))
5b467bf4
SM
210 (let ((rfs (read-from-string string i)))
211 (cons (car rfs)
6d57b1a3
SM
212 (cvs-string->strings (substring string (cdr rfs))
213 sep)))))))
5b467bf4 214
f1180544 215;;;;
5b467bf4 216;;;; file names
f1180544 217;;;;
5b467bf4
SM
218
219(defsubst cvs-expand-dir-name (d)
220 (file-name-as-directory (expand-file-name d)))
221
222;;;;
223;;;; (interactive <foo>) support function
224;;;;
225
226(defstruct (cvs-qtypedesc
227 (:constructor nil) (:copier nil)
228 (:constructor cvs-qtypedesc-create
229 (str2obj obj2str &optional complete hist-sym require)))
230 str2obj
231 obj2str
232 hist-sym
233 complete
234 require)
235
236
237(defconst cvs-qtypedesc-string1 (cvs-qtypedesc-create 'identity 'identity t))
238(defconst cvs-qtypedesc-string (cvs-qtypedesc-create 'identity 'identity))
239(defconst cvs-qtypedesc-strings
240 (cvs-qtypedesc-create 'cvs-string->strings 'cvs-strings->string nil))
241
242(defun cvs-query-read (default prompt qtypedesc &optional hist-sym)
243 (let* ((qtypedesc (or qtypedesc cvs-qtypedesc-strings))
244 (hist-sym (or hist-sym (cvs-qtypedesc-hist-sym qtypedesc)))
245 (complete (cvs-qtypedesc-complete qtypedesc))
246 (completions (and (functionp complete) (funcall complete)))
247 (initval (funcall (cvs-qtypedesc-obj2str qtypedesc) default)))
248 (funcall (cvs-qtypedesc-str2obj qtypedesc)
249 (cond
250 ((null complete) (read-string prompt initval hist-sym))
251 ((functionp complete)
252 (completing-read prompt completions
253 nil (cvs-qtypedesc-require qtypedesc)
254 initval hist-sym))
255 (t initval)))))
256
f1180544 257;;;;
5b467bf4 258;;;; Flags handling
f1180544 259;;;;
5b467bf4
SM
260
261(defstruct (cvs-flags
262 (:constructor nil)
263 (:constructor -cvs-flags-make
264 (desc defaults &optional qtypedesc hist-sym)))
265 defaults persist desc qtypedesc hist-sym)
266
267(defmacro cvs-flags-define (sym defaults
268 &optional desc qtypedesc hist-sym docstring)
269 `(defconst ,sym
270 (let ((bound (boundp ',sym)))
271 (if (and bound (cvs-flags-p ,sym)) ,sym
272 (let ((defaults ,defaults))
273 (-cvs-flags-make ,desc
274 (if bound (cons ,sym (cdr defaults)) defaults)
275 ,qtypedesc ,hist-sym))))
276 ,docstring))
277
278(defun cvs-flags-query (sym &optional desc arg)
279 "Query flags based on SYM.
e05a39ba 280Optional argument DESC will be used for the prompt.
5b467bf4
SM
281If ARG (or a prefix argument) is nil, just use the 0th default.
282If it is a non-negative integer, use the corresponding default.
283If it is a negative integer query for a new value of the corresponding
284 default and return that new value.
285If it is \\[universal-argument], just query and return a value without
286 altering the defaults.
287If it is \\[universal-argument] \\[universal-argument], behave just
288 as if a negative zero was provided."
289 (let* ((flags (symbol-value sym))
290 (desc (or desc (cvs-flags-desc flags)))
291 (qtypedesc (cvs-flags-qtypedesc flags))
292 (hist-sym (cvs-flags-hist-sym flags))
293 (arg (if (eq arg 'noquery) 0 (or arg current-prefix-arg 0)))
294 (numarg (prefix-numeric-value arg))
295 (defaults (cvs-flags-defaults flags))
296 (permstr (if (< numarg 0) (format " (%sth default)" (- numarg)))))
297 ;; special case for universal-argument
298 (when (consp arg)
299 (setq permstr (if (> numarg 4) " (permanent)" ""))
300 (setq numarg 0))
301
302 ;; sanity check
303 (unless (< (abs numarg) (length defaults))
3afbc435 304 (error "There is no %sth default" (abs numarg)))
5b467bf4
SM
305
306 (if permstr
307 (let* ((prompt (format "%s%s: " desc permstr))
308 (fs (cvs-query-read (nth (- numarg) (cvs-flags-defaults flags))
309 prompt qtypedesc hist-sym)))
310 (when (not (equal permstr ""))
311 (setf (nth (- numarg) (cvs-flags-defaults flags)) fs))
312 fs)
313 (nth numarg defaults))))
314
315(defsubst cvs-flags-set (sym index value)
316 "Set SYM's INDEX'th setting to VALUE."
317 (setf (nth index (cvs-flags-defaults (symbol-value sym))) value))
318
f1180544 319;;;;
5b467bf4 320;;;; Prefix keys
f1180544 321;;;;
5b467bf4
SM
322
323(defconst cvs-prefix-number 10)
324
325(defsubst cvs-prefix-sym (sym) (intern (concat (symbol-name sym) "-cps")))
326
327(defmacro cvs-prefix-define (sym docstring desc defaults
328 &optional qtypedesc hist-sym)
329 (let ((cps (cvs-prefix-sym sym)))
330 `(progn
630784a2 331 (defvar ,sym nil ,(concat (or docstring "") "
5b467bf4 332See `cvs-prefix-set' for further description of the behavior."))
08171162 333 (defvar ,cps
5b467bf4
SM
334 (let ((defaults ,defaults))
335 ;; sanity ensurance
336 (unless (>= (length defaults) cvs-prefix-number)
337 (setq defaults (append defaults
338 (make-list (1- cvs-prefix-number)
dedffa6a 339 (nth 0 defaults)))))
5b467bf4
SM
340 (-cvs-flags-make ,desc defaults ,qtypedesc ,hist-sym))))))
341
342(defun cvs-prefix-make-local (sym)
343 (let ((cps (cvs-prefix-sym sym)))
344 (make-local-variable sym)
345 (set (make-local-variable cps) (copy-cvs-flags (symbol-value cps)))))
346
347(defun cvs-prefix-set (sym arg)
348 ;; we could distinguish between numeric and non-numeric prefix args instead of
349 ;; relying on that magic `4'.
350 "Set the cvs-prefix contained in SYM.
351If ARG is between 0 and 9, it selects the corresponding default.
352If ARG is negative (or \\[universal-argument] which corresponds to negative 0),
353 it queries the user and sets the -ARG'th default.
354If ARG is greater than 9 (or \\[universal-argument] \\[universal-argument]),
355 the (ARG mod 10)'th prefix is made persistent.
0ff9b955 356If ARG is nil toggle the PREFIX's value between its 0th default and nil
5b467bf4
SM
357 and reset the persistence."
358 (let* ((prefix (symbol-value (cvs-prefix-sym sym)))
359 (numarg (if (integerp arg) arg 0))
360 (defs (cvs-flags-defaults prefix)))
361
362 ;; set persistence if requested
363 (when (> (prefix-numeric-value arg) 9)
364 (setf (cvs-flags-persist prefix) t)
365 (setq numarg (mod numarg 10)))
366
367 ;; set the value
368 (set sym
369 (cond
370 ((null arg)
371 (setf (cvs-flags-persist prefix) nil)
dedffa6a 372 (unless (symbol-value sym) (nth 0 (cvs-flags-defaults prefix))))
5b467bf4
SM
373
374 ((or (consp arg) (< numarg 0))
375 (setf (nth (- numarg) (cvs-flags-defaults prefix))
376 (cvs-query-read (nth (- numarg) (cvs-flags-defaults prefix))
377 (format "%s: " (cvs-flags-desc prefix))
378 (cvs-flags-qtypedesc prefix)
379 (cvs-flags-hist-sym prefix))))
380 (t (nth numarg (cvs-flags-defaults prefix)))))
381 (force-mode-line-update)))
382
383(defun cvs-prefix-get (sym &optional read-only)
384 "Return the current value of the prefix SYM.
e05a39ba 385And reset it unless READ-ONLY is non-nil."
5b467bf4
SM
386 (prog1 (symbol-value sym)
387 (unless (or read-only
388 (cvs-flags-persist (symbol-value (cvs-prefix-sym sym))))
389 (set sym nil)
390 (force-mode-line-update))))
391
392(provide 'pcvs-util)
393
ac62b9e4 394;; arch-tag: 3b2588bb-2ae3-4f1f-bf5b-dea91b1f8a59
3afbc435 395;;; pcvs-util.el ends here