Move defvars out of eval-when-compile. Use dolist.
[bpt/emacs.git] / lisp / array.el
CommitLineData
55535639 1;;; array.el --- array editing commands for GNU Emacs
c0274f38 2
0d30b337
TTN
3;; Copyright (C) 1987, 2000, 2002, 2003, 2004,
4;; 2005 Free Software Foundation, Inc.
9750e079 5
e5167999
ER
6;; Author David M. Brown
7;; Maintainer: FSF
e9571d2a 8;; Keywords: extensions
b1d6ae0b 9
b1d6ae0b
JB
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
e5167999 14;; the Free Software Foundation; either version 2, or (at your option)
b1d6ae0b
JB
15;; any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
b578f267 23;; along with GNU Emacs; see the file COPYING. If not, write to the
086add15
LK
24;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25;; Boston, MA 02110-1301, USA.
b1d6ae0b 26
e5167999
ER
27;;; Commentary:
28
e41b2db1
ER
29;; Commands for editing a buffer interpreted as a rectangular array
30;; or matrix of whitespace-separated strings. You specify the array
31;; dimensions and some other parameters at startup time.
32
33;; Written by dmb%morgoth@harvard.harvard.edu (address is old)
34;; (David M. Brown at Goldberg-Zoino & Associates, Inc.)
35;; Thanks to cph@kleph.ai.mit.edu for assistance
36
37;; To do:
38;; Smooth initialization process by grokking local variables list
39;; at end of buffer or parsing buffer using whitespace as delimiters.
40;; Make 'array-copy-column-right faster.
b1d6ae0b
JB
41
42\f
e5167999 43;;; Code:
b1d6ae0b 44
41674a5a
DL
45(eval-when-compile
46 (defvar array-max-column)
47 (defvar array-columns-per-line)
48 (defvar array-buffer-column)
49 (defvar array-line-length)
50 (defvar array-buffer-line)
51 (defvar array-lines-per-row)
52 (defvar array-max-row)
53 (defvar array-field-width)
54 (defvar array-row)
55 (defvar array-column)
56 (defvar array-rows-numbered)
57 (defvar array-copy-string)
58 (defvar array-init-field)
59 (defvar array-respect-tabs))
60
b1d6ae0b 61;;; Internal information functions.
71296446 62
b1d6ae0b 63(defun array-cursor-in-array-range ()
41674a5a 64 "Return t if the cursor is in a valid array cell.
b1d6ae0b 65Its ok to be on a row number line."
41674a5a
DL
66 (let ((columns-last-line (% array-max-column array-columns-per-line)))
67 ;; Requires array-buffer-line and array-buffer-column to be current.
b1d6ae0b
JB
68 (not (or
69 ;; The cursor is too far to the right.
41674a5a 70 (>= array-buffer-column array-line-length)
b1d6ae0b 71 ;; The cursor is below the last row.
41674a5a 72 (>= array-buffer-line (* array-lines-per-row array-max-row))
b1d6ae0b
JB
73 ;; The cursor is on the last line of the row, the line is smaller
74 ;; than the others, and the cursor is after the last array column
75 ;; on the line.
41674a5a 76 (and (zerop (% (1+ array-buffer-line) array-lines-per-row))
b1d6ae0b 77 (not (zerop columns-last-line))
41674a5a 78 (>= array-buffer-column (* columns-last-line array-field-width)))))))
b1d6ae0b
JB
79
80(defun array-current-row ()
81 "Return the array row of the field in which the cursor is located."
41674a5a 82 ;; Requires array-buffer-line and array-buffer-column to be current.
b1d6ae0b 83 (and (array-cursor-in-array-range)
41674a5a 84 (1+ (floor array-buffer-line array-lines-per-row))))
b1d6ae0b
JB
85
86(defun array-current-column ()
87 "Return the array column of the field in which the cursor is located."
41674a5a 88 ;; Requires array-buffer-line and array-buffer-column to be current.
b1d6ae0b
JB
89 (and (array-cursor-in-array-range)
90 ;; It's not okay to be on a row number line.
41674a5a
DL
91 (not (and array-rows-numbered
92 (zerop (% array-buffer-line array-lines-per-row))))
b1d6ae0b
JB
93 (+
94 ;; Array columns due to line differences.
41674a5a
DL
95 (* array-columns-per-line
96 (if array-rows-numbered
97 (1- (% array-buffer-line array-lines-per-row))
98 (% array-buffer-line array-lines-per-row)))
b1d6ae0b 99 ;; Array columns on the current line.
41674a5a 100 (1+ (floor array-buffer-column array-field-width)))))
b1d6ae0b
JB
101
102(defun array-update-array-position (&optional a-row a-column)
41674a5a
DL
103 "Set `array-row' and `array-column' to their current values.
104Set them to the optional arguments A-ROW and A-COLUMN if those are supplied."
105 ;; Requires that array-buffer-line and array-buffer-column be current.
b1d6ae0b
JB
106 (setq array-row (or a-row (array-current-row))
107 array-column (or a-column (array-current-column))))
108
109(defun array-update-buffer-position ()
41674a5a
DL
110 "Set array-buffer-line and array-buffer-column to their current values."
111 (setq array-buffer-line (current-line)
112 array-buffer-column (current-column)))
b1d6ae0b
JB
113
114\f
115
116;;; Information commands.
117
118(defun array-what-position ()
119 "Display the row and column in which the cursor is positioned."
120 (interactive)
41674a5a
DL
121 (let ((array-buffer-line (current-line))
122 (array-buffer-column (current-column)))
123 (message "Array row: %s Array column: %s"
4bf785ba
KH
124 (prin1-to-string (array-current-row))
125 (prin1-to-string (array-current-column)))))
b1d6ae0b
JB
126
127(defun array-display-local-variables ()
128 "Display the current state of the local variables in the minibuffer."
129 (interactive)
130 (let ((buf (buffer-name (current-buffer))))
131 (with-output-to-temp-buffer "*Local Variables*"
132 (buffer-disable-undo standard-output)
133 (terpri)
134 (princ (format " Buffer: %s\n\n" buf))
135 (princ (format " max-row: %s\n"
41674a5a 136 (prin1-to-string array-max-row)))
b1d6ae0b 137 (princ (format " max-column: %s\n"
41674a5a 138 (prin1-to-string array-max-column)))
b1d6ae0b 139 (princ (format " columns-per-line: %s\n"
41674a5a 140 (prin1-to-string array-columns-per-line)))
b1d6ae0b 141 (princ (format " field-width: %s\n"
41674a5a 142 (prin1-to-string array-field-width)))
b1d6ae0b 143 (princ (format " rows-numbered: %s\n"
41674a5a 144 (prin1-to-string array-rows-numbered)))
b1d6ae0b 145 (princ (format " lines-per-row: %s\n"
41674a5a 146 (prin1-to-string array-lines-per-row)))
b1d6ae0b 147 (princ (format " line-length: %s\n"
41674a5a 148 (prin1-to-string array-line-length))))))
b1d6ae0b
JB
149
150\f
151
152;;; Internal movement functions.
153
154(defun array-beginning-of-field (&optional go-there)
155 "Return the column of the beginning of the current field.
156Optional argument GO-THERE, if non-nil, means go there too."
41674a5a
DL
157 ;; Requires that array-buffer-column be current.
158 (let ((goal-column (- array-buffer-column (% array-buffer-column array-field-width))))
b1d6ae0b
JB
159 (if go-there
160 (move-to-column-untabify goal-column)
161 goal-column)))
162
163(defun array-end-of-field (&optional go-there)
164 "Return the column of the end of the current array field.
165If optional argument GO-THERE is non-nil, go there too."
41674a5a
DL
166 ;; Requires that array-buffer-column be current.
167 (let ((goal-column (+ (- array-buffer-column (% array-buffer-column array-field-width))
168 array-field-width)))
b1d6ae0b
JB
169 (if go-there
170 (move-to-column-untabify goal-column)
171 goal-column)))
172
173(defun array-move-to-cell (a-row a-column)
41674a5a 174 "Move to array row A-ROW and array column A-COLUMN.
b1d6ae0b 175Leave point at the beginning of the field and return the new buffer column."
41674a5a
DL
176 (let ((goal-line (+ (* array-lines-per-row (1- a-row))
177 (if array-rows-numbered 1 0)
178 (floor (1- a-column) array-columns-per-line)))
179 (goal-column (* array-field-width (% (1- a-column) array-columns-per-line))))
b1d6ae0b
JB
180 (goto-char (point-min))
181 (forward-line goal-line)
182 (move-to-column-untabify goal-column)))
183
184(defun array-move-to-row (a-row)
185 "Move to array row A-ROW preserving the current array column.
186Leave point at the beginning of the field and return the new array row."
41674a5a
DL
187 ;; Requires that array-buffer-line and array-buffer-column be current.
188 (let ((goal-line (+ (* array-lines-per-row (1- a-row))
189 (% array-buffer-line array-lines-per-row)))
190 (goal-column (- array-buffer-column (% array-buffer-column array-field-width))))
191 (forward-line (- goal-line array-buffer-line))
b1d6ae0b
JB
192 (move-to-column-untabify goal-column)
193 a-row))
194
195(defun array-move-to-column (a-column)
196 "Move to array column A-COLUMN preserving the current array row.
197Leave point at the beginning of the field and return the new array column."
41674a5a
DL
198 ;; Requires that array-buffer-line and array-buffer-column be current.
199 (let ((goal-line (+ (- array-buffer-line (% array-buffer-line array-lines-per-row))
200 (if array-rows-numbered 1 0)
201 (floor (1- a-column) array-columns-per-line)))
202 (goal-column (* array-field-width (% (1- a-column) array-columns-per-line))))
203 (forward-line (- goal-line array-buffer-line))
b1d6ae0b
JB
204 (move-to-column-untabify goal-column)
205 a-column))
206
207(defun array-move-one-row (sign)
208 "Move one array row in direction SIGN (1 or -1).
209Leave point at the beginning of the field and return the new array row.
210If requested to move beyond the array bounds, signal an error."
41674a5a 211 ;; Requires that array-buffer-line and array-buffer-column be current.
b1d6ae0b
JB
212 (let ((goal-column (array-beginning-of-field))
213 (array-row (or (array-current-row)
41674a5a
DL
214 (error "Cursor is not in a valid array cell"))))
215 (cond ((and (= array-row array-max-row) (= sign 1))
216 (error "End of array"))
b1d6ae0b 217 ((and (= array-row 1) (= sign -1))
41674a5a 218 (error "Beginning of array"))
b1d6ae0b
JB
219 (t
220 (progn
41674a5a 221 (forward-line (* sign array-lines-per-row))
b1d6ae0b
JB
222 (move-to-column-untabify goal-column)
223 (+ array-row sign))))))
224
225(defun array-move-one-column (sign)
226 "Move one array column in direction SIGN (1 or -1).
227Leave point at the beginning of the field and return the new array column.
228If requested to move beyond the array bounds, signal an error."
41674a5a 229 ;; Requires that array-buffer-line and array-buffer-column be current.
b1d6ae0b 230 (let ((array-column (or (array-current-column)
41674a5a
DL
231 (error "Cursor is not in a valid array cell"))))
232 (cond ((and (= array-column array-max-column) (= sign 1))
233 (error "End of array"))
b1d6ae0b 234 ((and (= array-column 1) (= sign -1))
41674a5a 235 (error "Beginning of array"))
b1d6ae0b 236 (t
41674a5a 237 (cond
b1d6ae0b 238 ;; Going backward from first column on the line.
41674a5a 239 ((and (= sign -1) (= 1 (% array-column array-columns-per-line)))
b1d6ae0b
JB
240 (forward-line -1)
241 (move-to-column-untabify
41674a5a 242 (* array-field-width (1- array-columns-per-line))))
b1d6ae0b 243 ;; Going forward from last column on the line.
41674a5a 244 ((and (= sign 1) (zerop (% array-column array-columns-per-line)))
b1d6ae0b
JB
245 (forward-line 1))
246 ;; Somewhere in the middle of the line.
247 (t
248 (move-to-column-untabify (+ (array-beginning-of-field)
41674a5a 249 (* array-field-width sign)))))
b1d6ae0b
JB
250 (+ array-column sign)))))
251
252(defun array-normalize-cursor ()
41674a5a
DL
253 "Move the cursor to the first non-whitespace character in the field.
254If necessary, scroll horizontally to keep the cursor in view."
b1d6ae0b 255 ;; Assumes point is at the beginning of the field.
41674a5a 256 (let ((array-buffer-column (current-column)))
b1d6ae0b
JB
257 (skip-chars-forward " \t"
258 (1- (save-excursion (array-end-of-field t) (point))))
259 (array-maybe-scroll-horizontally)))
260
261(defun array-maybe-scroll-horizontally ()
262 "If necessary, scroll horizontally to keep the cursor in view."
263 ;; This is only called from array-normalize-cursor so
41674a5a 264 ;; array-buffer-column will always be current.
b1d6ae0b
JB
265 (let ((w-hscroll (window-hscroll))
266 (w-width (window-width)))
267 (cond
41674a5a
DL
268 ((and (>= array-buffer-column w-hscroll)
269 (<= array-buffer-column (+ w-hscroll w-width)))
b1d6ae0b
JB
270 ;; It's already visible. Do nothing.
271 nil)
41674a5a 272 ((> array-buffer-column (+ w-hscroll w-width))
b1d6ae0b 273 ;; It's to the right. Scroll left.
41674a5a 274 (scroll-left (- (- array-buffer-column w-hscroll)
b1d6ae0b
JB
275 (/ w-width 2))))
276 (t
277 ;; It's to the left. Scroll right.
41674a5a 278 (scroll-right (+ (- w-hscroll array-buffer-column)
b1d6ae0b
JB
279 (/ w-width 2)))))))
280
281\f
282
283;;; Movement commands.
284
285(defun array-next-row (&optional arg)
286 "Move down one array row, staying in the current array column.
287If optional ARG is given, move down ARG array rows."
288 (interactive "p")
41674a5a
DL
289 (let ((array-buffer-line (current-line))
290 (array-buffer-column (current-column)))
b1d6ae0b
JB
291 (if (= (abs arg) 1)
292 (array-move-one-row arg)
293 (array-move-to-row
294 (limit-index (+ (or (array-current-row)
41674a5a 295 (error "Cursor is not in an array cell"))
b1d6ae0b 296 arg)
41674a5a 297 array-max-row))))
b1d6ae0b
JB
298 (array-normalize-cursor))
299
300(defun array-previous-row (&optional arg)
301 "Move up one array row, staying in the current array column.
302If optional ARG is given, move up ARG array rows."
303 (interactive "p")
304 (array-next-row (- arg)))
305
306(defun array-forward-column (&optional arg)
307 "Move forward one field, staying in the current array row.
308If optional ARG is given, move forward ARG array columns.
309If necessary, keep the cursor in the window by scrolling right or left."
310 (interactive "p")
41674a5a
DL
311 (let ((array-buffer-line (current-line))
312 (array-buffer-column (current-column)))
b1d6ae0b
JB
313 (if (= (abs arg) 1)
314 (array-move-one-column arg)
315 (array-move-to-column
316 (limit-index (+ (or (array-current-column)
41674a5a 317 (error "Cursor is not in an array cell"))
b1d6ae0b 318 arg)
41674a5a 319 array-max-column))))
b1d6ae0b
JB
320 (array-normalize-cursor))
321
322(defun array-backward-column (&optional arg)
323 "Move backward one field, staying in the current array row.
324If optional ARG is given, move backward ARG array columns.
325If necessary, keep the cursor in the window by scrolling right or left."
326 (interactive "p")
327 (array-forward-column (- arg)))
328
329(defun array-goto-cell (a-row a-column)
330 "Go to array row A-ROW and array column A-COLUMN."
331 (interactive "nArray row: \nnArray column: ")
332 (array-move-to-cell
41674a5a
DL
333 (limit-index a-row array-max-row)
334 (limit-index a-column array-max-column))
b1d6ae0b
JB
335 (array-normalize-cursor))
336
337\f
338
339;;; Internal copying functions.
340
341(defun array-field-string ()
342 "Return the field string at the current cursor location."
41674a5a 343 ;; Requires that array-buffer-column be current.
b1d6ae0b
JB
344 (buffer-substring
345 (save-excursion (array-beginning-of-field t) (point))
346 (save-excursion (array-end-of-field t) (point))))
347
348(defun array-copy-once-vertically (sign)
349 "Copy the current field into one array row in direction SIGN (1 or -1).
350Leave point at the beginning of the field and return the new array row.
351If requested to move beyond the array bounds, signal an error."
41674a5a 352 ;; Requires that array-buffer-line, array-buffer-column, and array-copy-string be current.
b1d6ae0b
JB
353 (let ((a-row (array-move-one-row sign)))
354 (let ((inhibit-quit t))
355 (delete-region (point) (save-excursion (array-end-of-field t) (point)))
41674a5a
DL
356 (insert array-copy-string))
357 (move-to-column array-buffer-column)
b1d6ae0b
JB
358 a-row))
359
360(defun array-copy-once-horizontally (sign)
361 "Copy the current field into one array column in direction SIGN (1 or -1).
362Leave point at the beginning of the field and return the new array column.
363If requested to move beyond the array bounds, signal an error."
41674a5a 364 ;; Requires that array-buffer-line, array-buffer-column, and array-copy-string be current.
b1d6ae0b
JB
365 (let ((a-column (array-move-one-column sign)))
366 (array-update-buffer-position)
367 (let ((inhibit-quit t))
368 (delete-region (point) (save-excursion (array-end-of-field t) (point)))
41674a5a
DL
369 (insert array-copy-string))
370 (move-to-column array-buffer-column)
b1d6ae0b
JB
371 a-column))
372
373(defun array-copy-to-row (a-row)
374 "Copy the current field vertically into every cell up to and including A-ROW.
375Leave point at the beginning of the field."
41674a5a
DL
376 ;; Requires that array-buffer-line, array-buffer-column, array-row, and
377 ;; array-copy-string be current.
b1d6ae0b
JB
378 (let* ((num (- a-row array-row))
379 (count (abs num))
380 (sign (if (zerop count) () (/ num count))))
381 (while (> count 0)
382 (array-move-one-row sign)
383 (array-update-buffer-position)
384 (let ((inhibit-quit t))
385 (delete-region (point) (save-excursion (array-end-of-field t) (point)))
41674a5a
DL
386 (insert array-copy-string))
387 (move-to-column array-buffer-column)
b1d6ae0b 388 (setq count (1- count)))))
71296446 389
b1d6ae0b 390(defun array-copy-to-column (a-column)
41674a5a
DL
391 "Copy current field horizontally into every cell up to and including A-COLUMN.
392Leave point at the beginning of the field."
393 ;; Requires that array-buffer-line, array-buffer-column, array-column, and
394 ;; array-copy-string be current.
b1d6ae0b
JB
395 (let* ((num (- a-column array-column))
396 (count (abs num))
397 (sign (if (zerop count) () (/ num count))))
398 (while (> count 0)
399 (array-move-one-column sign)
400 (array-update-buffer-position)
401 (let ((inhibit-quit t))
402 (delete-region (point) (save-excursion (array-end-of-field t) (point)))
41674a5a
DL
403 (insert array-copy-string))
404 (move-to-column array-buffer-column)
b1d6ae0b
JB
405 (setq count (1- count)))))
406
407(defun array-copy-to-cell (a-row a-column)
408 "Copy the current field into the cell at A-ROW, A-COLUMN.
409Leave point at the beginning of the field."
41674a5a 410 ;; Requires that array-copy-string be current.
b1d6ae0b
JB
411 (array-move-to-cell a-row a-column)
412 (array-update-buffer-position)
413 (delete-region (point) (save-excursion (array-end-of-field t) (point)))
41674a5a
DL
414 (insert array-copy-string)
415 (move-to-column array-buffer-column))
b1d6ae0b
JB
416
417\f
418
419;;; Commands for copying.
420
421(defun array-copy-down (&optional arg)
422 "Copy the current field one array row down.
423If optional ARG is given, copy down through ARG array rows."
424 (interactive "p")
41674a5a
DL
425 (let* ((array-buffer-line (current-line))
426 (array-buffer-column (current-column))
b1d6ae0b 427 (array-row (or (array-current-row)
41674a5a
DL
428 (error "Cursor is not in a valid array cell")))
429 (array-copy-string (array-field-string)))
b1d6ae0b
JB
430 (if (= (abs arg) 1)
431 (array-copy-once-vertically arg)
432 (array-copy-to-row
41674a5a 433 (limit-index (+ array-row arg) array-max-row))))
b1d6ae0b
JB
434 (array-normalize-cursor))
435
436(defun array-copy-up (&optional arg)
437 "Copy the current field one array row up.
438If optional ARG is given, copy up through ARG array rows."
439 (interactive "p")
440 (array-copy-down (- arg)))
441
442(defun array-copy-forward (&optional arg)
443 "Copy the current field one array column to the right.
444If optional ARG is given, copy through ARG array columns to the right."
445 (interactive "p")
41674a5a
DL
446 (let* ((array-buffer-line (current-line))
447 (array-buffer-column (current-column))
b1d6ae0b 448 (array-column (or (array-current-column)
41674a5a
DL
449 (error "Cursor is not in a valid array cell")))
450 (array-copy-string (array-field-string)))
b1d6ae0b
JB
451 (if (= (abs arg) 1)
452 (array-copy-once-horizontally arg)
453 (array-copy-to-column
41674a5a 454 (limit-index (+ array-column arg) array-max-column))))
b1d6ae0b
JB
455 (array-normalize-cursor))
456
457(defun array-copy-backward (&optional arg)
458 "Copy the current field one array column to the left.
459If optional ARG is given, copy through ARG array columns to the left."
460 (interactive "p")
461 (array-copy-forward (- arg)))
462
463(defun array-copy-column-forward (&optional arg)
464 "Copy the entire current column in to the column to the right.
465If optional ARG is given, copy through ARG array columns to the right."
466 (interactive "p")
467 (array-update-buffer-position)
468 (array-update-array-position)
469 (if (not array-column)
41674a5a 470 (error "Cursor is not in a valid array cell"))
b1d6ae0b
JB
471 (message "Working...")
472 (let ((this-row 0))
41674a5a 473 (while (< this-row array-max-row)
b1d6ae0b
JB
474 (setq this-row (1+ this-row))
475 (array-move-to-cell this-row array-column)
476 (array-update-buffer-position)
41674a5a 477 (let ((array-copy-string (array-field-string)))
b1d6ae0b
JB
478 (if (= (abs arg) 1)
479 (array-copy-once-horizontally arg)
480 (array-copy-to-column
41674a5a 481 (limit-index (+ array-column arg) array-max-column))))))
b1d6ae0b
JB
482 (message "Working...done")
483 (array-move-to-row array-row)
484 (array-normalize-cursor))
485
486(defun array-copy-column-backward (&optional arg)
487 "Copy the entire current column one column to the left.
488If optional ARG is given, copy through ARG columns to the left."
489 (interactive "p")
490 (array-copy-column-forward (- arg)))
491
492(defun array-copy-row-down (&optional arg)
493 "Copy the entire current row one row down.
494If optional ARG is given, copy through ARG rows down."
495 (interactive "p")
496 (array-update-buffer-position)
497 (array-update-array-position)
498 (if (not array-row)
41674a5a 499 (error "Cursor is not in a valid array cell"))
b1d6ae0b
JB
500 (cond
501 ((and (= array-row 1) (= arg -1))
41674a5a
DL
502 (error "Beginning of array"))
503 ((and (= array-row array-max-row) (= arg 1))
504 (error "End of array"))
b1d6ae0b 505 (t
41674a5a 506 (let* ((array-copy-string
b1d6ae0b
JB
507 (buffer-substring
508 (save-excursion (array-move-to-cell array-row 1)
509 (point))
41674a5a 510 (save-excursion (array-move-to-cell array-row array-max-column)
b1d6ae0b
JB
511 (forward-line 1)
512 (point))))
513 (this-row array-row)
41674a5a 514 (goal-row (limit-index (+ this-row arg) array-max-row))
b1d6ae0b
JB
515 (num (- goal-row this-row))
516 (count (abs num))
517 (sign (if (not (zerop count)) (/ num count))))
518 (while (> count 0)
519 (setq this-row (+ this-row sign))
520 (array-move-to-cell this-row 1)
521 (let ((inhibit-quit t))
522 (delete-region (point)
523 (save-excursion
41674a5a 524 (array-move-to-cell this-row array-max-column)
b1d6ae0b
JB
525 (forward-line 1)
526 (point)))
41674a5a 527 (insert array-copy-string))
b1d6ae0b
JB
528 (setq count (1- count)))
529 (array-move-to-cell goal-row (or array-column 1)))))
530 (array-normalize-cursor))
531
532(defun array-copy-row-up (&optional arg)
533 "Copy the entire current array row into the row above.
534If optional ARG is given, copy through ARG rows up."
535 (interactive "p")
536 (array-copy-row-down (- arg)))
537
538(defun array-fill-rectangle ()
539 "Copy the field at mark into every cell between mark and point."
540 (interactive)
541 ;; Bind arguments.
542 (array-update-buffer-position)
543 (let ((p-row (or (array-current-row)
41674a5a 544 (error "Cursor is not in a valid array cell")))
b1d6ae0b 545 (p-column (or (array-current-column)
41674a5a 546 (error "Cursor is not in a valid array cell")))
b1d6ae0b
JB
547 (m-row
548 (save-excursion
549 (exchange-point-and-mark)
550 (array-update-buffer-position)
551 (or (array-current-row)
41674a5a
DL
552 (error "Mark is not in a valid array cell"))))
553 (m-column
b1d6ae0b
JB
554 (save-excursion
555 (exchange-point-and-mark)
556 (array-update-buffer-position)
557 (or (array-current-column)
41674a5a 558 (error "Mark is not in a valid array cell")))))
b1d6ae0b
JB
559 (message "Working...")
560 (let ((top-row (min m-row p-row))
561 (bottom-row (max m-row p-row))
562 (left-column (min m-column p-column))
563 (right-column (max m-column p-column)))
564 ;; Do the first row.
41674a5a 565 (let ((array-copy-string
b1d6ae0b
JB
566 (save-excursion
567 (array-move-to-cell m-row m-column)
568 (array-update-buffer-position)
569 (array-field-string))))
570 (array-copy-to-cell top-row left-column)
571 (array-update-array-position top-row left-column)
572 (array-update-buffer-position)
573 (array-copy-to-column right-column))
574 ;; Do the rest of the rows.
575 (array-move-to-cell top-row left-column)
41674a5a 576 (let ((array-copy-string
b1d6ae0b
JB
577 (buffer-substring
578 (point)
579 (save-excursion
580 (array-move-to-cell top-row right-column)
41674a5a 581 (setq array-buffer-column (current-column))
b1d6ae0b
JB
582 (array-end-of-field t)
583 (point))))
584 (this-row top-row))
585 (while (/= this-row bottom-row)
586 (setq this-row (1+ this-row))
587 (array-move-to-cell this-row left-column)
588 (let ((inhibit-quit t))
589 (delete-region
590 (point)
591 (save-excursion
592 (array-move-to-cell this-row right-column)
41674a5a 593 (setq array-buffer-column (current-column))
b1d6ae0b
JB
594 (array-end-of-field t)
595 (point)))
41674a5a 596 (insert array-copy-string)))))
b1d6ae0b
JB
597 (message "Working...done")
598 (array-goto-cell p-row p-column)))
599
600\f
601
602;;; Reconfiguration of the array.
603
604(defun array-make-template ()
605 "Create the template of an array."
606 (interactive)
41674a5a 607 ;; If there is a conflict between array-field-width and init-string, resolve it.
b1d6ae0b
JB
608 (let ((check t)
609 (len))
610 (while check
3981e5b5 611 (setq array-init-field (read-string "Initial field value: "))
41674a5a
DL
612 (setq len (length array-init-field))
613 (if (/= len array-field-width)
b1d6ae0b 614 (if (y-or-n-p (format "Change field width to %d? " len))
41674a5a 615 (progn (setq array-field-width len)
b1d6ae0b
JB
616 (setq check nil)))
617 (setq check nil))))
618 (goto-char (point-min))
619 (message "Working...")
620 (let ((this-row 1))
621 ;; Loop through the rows.
41674a5a
DL
622 (while (<= this-row array-max-row)
623 (if array-rows-numbered
b1d6ae0b
JB
624 (insert (format "%d:\n" this-row)))
625 (let ((this-column 1))
626 ;; Loop through the columns.
41674a5a
DL
627 (while (<= this-column array-max-column)
628 (insert array-init-field)
629 (if (and (zerop (% this-column array-columns-per-line))
630 (/= this-column array-max-column))
b1d6ae0b
JB
631 (newline))
632 (setq this-column (1+ this-column))))
633 (setq this-row (1+ this-row))
634 (newline)))
635 (message "Working...done")
636 (array-goto-cell 1 1))
637
638(defun array-reconfigure-rows (new-columns-per-line new-rows-numbered)
41674a5a
DL
639 "Reconfigure the state of `array-rows-numbered' and `array-columns-per-line'.
640NEW-COLUMNS-PER-LINE is the desired value of `array-columns-per-line' and
b1d6ae0b 641NEW-ROWS-NUMBERED (a character, either ?y or ?n) is the desired value
26cf3f33 642of `array-rows-numbered'."
b1d6ae0b
JB
643 (interactive "nColumns per line: \ncRows numbered? (y or n) ")
644 ;; Check on new-columns-per-line
645 (let ((check t))
646 (while check
647 (if (and (>= new-columns-per-line 1)
41674a5a 648 (<= new-columns-per-line array-max-column))
b1d6ae0b
JB
649 (setq check nil)
650 (setq new-columns-per-line
027a4b6b 651 (string-to-number
3981e5b5 652 (read-string
41674a5a 653 (format "Columns per line (1 - %d): " array-max-column)))))))
b1d6ae0b
JB
654 ;; Check on new-rows-numbered. It has to be done this way
655 ;; because interactive does not have y-or-n-p.
656 (cond
657 ((eq new-rows-numbered ?y)
658 (setq new-rows-numbered t))
659 ((eq new-rows-numbered ?n)
660 (setq new-rows-numbered nil))
661 (t
662 (setq new-rows-numbered (y-or-n-p "Rows numbered? "))))
663 (message "Working...")
664 (array-update-buffer-position)
665 (let* ((main-buffer (buffer-name (current-buffer)))
18bf4ad8 666 (temp-buffer (generate-new-buffer " *Array*"))
41674a5a
DL
667 (temp-max-row array-max-row)
668 (temp-max-column array-max-column)
669 (old-rows-numbered array-rows-numbered)
670 (old-columns-per-line array-columns-per-line)
671 (old-lines-per-row array-lines-per-row)
672 (old-field-width array-field-width)
673 (old-line-length array-line-length)
b1d6ae0b
JB
674 (this-row 1))
675 (array-update-array-position)
676 ;; Do the cutting in a temporary buffer.
677 (copy-to-buffer temp-buffer (point-min) (point-max))
678 (set-buffer temp-buffer)
679 (goto-char (point-min))
680 (while (<= this-row temp-max-row)
681 ;; Deal with row number.
682 (cond
683 ((or (and old-rows-numbered new-rows-numbered)
684 (and (not old-rows-numbered) (not new-rows-numbered)))
685 ;; Nothing is changed.
686 ())
687 ((and old-rows-numbered (not new-rows-numbered))
688 ;; Delete the row number.
689 (kill-line 1))
690 (t
691 ;; Add the row number.
cc94298a 692 (insert (format "%d:\n" this-row))))
b1d6ae0b
JB
693 ;; Deal with the array columns in this row.
694 (cond
695 ((= old-columns-per-line new-columns-per-line)
696 ;; Nothing is changed. Go to the next row.
697 (forward-line (- old-lines-per-row (if old-rows-numbered 1 0))))
698 (t
699 ;; First expand the row. Then cut it up into new pieces.
700 (let ((newlines-to-be-removed
701 (floor (1- temp-max-column) old-columns-per-line))
702 (newlines-removed 0)
703 (newlines-to-be-added
704 (floor (1- temp-max-column) new-columns-per-line))
705 (newlines-added 0))
706 (while (< newlines-removed newlines-to-be-removed)
707 (move-to-column-untabify
708 (* (1+ newlines-removed) old-line-length))
709 (kill-line 1)
710 (setq newlines-removed (1+ newlines-removed)))
711 (beginning-of-line)
712 (while (< newlines-added newlines-to-be-added)
713 (move-to-column-untabify (* old-field-width new-columns-per-line))
714 (newline)
715 (setq newlines-added (1+ newlines-added)))
716 (forward-line 1))))
717 (setq this-row (1+ this-row)))
718 (let ((inhibit-quit t))
719 (set-buffer main-buffer)
720 (erase-buffer)
26cf3f33 721 (insert-buffer-substring temp-buffer)
b1d6ae0b 722 ;; Update local variables.
41674a5a
DL
723 (setq array-columns-per-line new-columns-per-line)
724 (setq array-rows-numbered new-rows-numbered)
725 (setq array-line-length (* old-field-width new-columns-per-line))
726 (setq array-lines-per-row
9cd77daa
PE
727 (+ (floor (1- temp-max-column) new-columns-per-line)
728 (if new-rows-numbered 2 1)))
b1d6ae0b
JB
729 (array-goto-cell (or array-row 1) (or array-column 1)))
730 (kill-buffer temp-buffer))
731 (message "Working...done"))
732
733(defun array-expand-rows ()
734 "Expand the rows so each fits on one line and remove row numbers."
735 (interactive)
41674a5a 736 (array-reconfigure-rows array-max-column ?n))
b1d6ae0b
JB
737
738\f
739
740;;; Utilities.
741
742(defun limit-index (index limit)
743 (cond ((< index 1) 1)
744 ((> index limit) limit)
745 (t index)))
746
b1d6ae0b 747(defun xor (pred1 pred2)
41674a5a 748 "Return the logical exclusive or of predicates PRED1 and PRED2."
b1d6ae0b
JB
749 (and (or pred1 pred2)
750 (not (and pred1 pred2))))
751
752(defun current-line ()
41674a5a 753 "Return the current buffer line at point. The first line is 0."
b1d6ae0b
JB
754 (save-excursion
755 (beginning-of-line)
756 (count-lines (point-min) (point))))
757
758(defun move-to-column-untabify (column)
759 "Move to COLUMN on the current line, untabifying if necessary.
760Return COLUMN."
761 (or (and (= column (move-to-column column))
762 column)
763 ;; There is a tab in the way.
41674a5a
DL
764 (if array-respect-tabs
765 (error "There is a TAB character in the way")
b1d6ae0b
JB
766 (progn
767 (untabify-backward)
768 (move-to-column column)))))
769
770(defun untabify-backward ()
26cf3f33 771 "Untabify the preceding TAB."
b1d6ae0b
JB
772 (save-excursion
773 (let ((start (point)))
774 (backward-char 1)
775 (untabify (point) start))))
776
777\f
778
779;;; Array mode.
780
41674a5a 781(defvar array-mode-map nil
b1d6ae0b
JB
782 "Keymap used in array mode.")
783
784(if array-mode-map
785 ()
786 (setq array-mode-map (make-keymap))
787 ;; Bind keys.
788 (define-key array-mode-map "\M-ad" 'array-display-local-variables)
789 (define-key array-mode-map "\M-am" 'array-make-template)
790 (define-key array-mode-map "\M-ae" 'array-expand-rows)
791 (define-key array-mode-map "\M-ar" 'array-reconfigure-rows)
792 (define-key array-mode-map "\M-a=" 'array-what-position)
793 (define-key array-mode-map "\M-ag" 'array-goto-cell)
794 (define-key array-mode-map "\M-af" 'array-fill-rectangle)
795 (define-key array-mode-map "\C-n" 'array-next-row)
796 (define-key array-mode-map "\C-p" 'array-previous-row)
797 (define-key array-mode-map "\C-f" 'array-forward-column)
798 (define-key array-mode-map "\C-b" 'array-backward-column)
799 (define-key array-mode-map "\M-n" 'array-copy-down)
800 (define-key array-mode-map "\M-p" 'array-copy-up)
801 (define-key array-mode-map "\M-f" 'array-copy-forward)
802 (define-key array-mode-map "\M-b" 'array-copy-backward)
803 (define-key array-mode-map "\M-\C-n" 'array-copy-row-down)
804 (define-key array-mode-map "\M-\C-p" 'array-copy-row-up)
805 (define-key array-mode-map "\M-\C-f" 'array-copy-column-forward)
806 (define-key array-mode-map "\M-\C-b" 'array-copy-column-backward))
807
808(put 'array-mode 'mode-class 'special)
809
499bfd5f 810;;;###autoload
b1d6ae0b
JB
811(defun array-mode ()
812 "Major mode for editing arrays.
813
814 Array mode is a specialized mode for editing arrays. An array is
815considered to be a two-dimensional set of strings. The strings are
816NOT recognized as integers or real numbers.
817
41674a5a 818 The array MUST reside at the top of the buffer.
b1d6ae0b
JB
819
820 TABs are not respected, and may be converted into spaces at any time.
26cf3f33 821Setting the variable `array-respect-tabs' to non-nil will prevent TAB conversion,
b1d6ae0b
JB
822but will cause many functions to give errors if they encounter one.
823
824 Upon entering array mode, you will be prompted for the values of
825several variables. Others will be calculated based on the values you
246e8695 826supply. These variables are all local to the buffer. Other buffer
b1d6ae0b
JB
827in array mode may have different values assigned to the variables.
828The variables are:
829
830Variables you assign:
41674a5a
DL
831 array-max-row: The number of rows in the array.
832 array-max-column: The number of columns in the array.
833 array-columns-per-line: The number of columns in the array per line of buffer.
834 array-field-width: The width of each field, in characters.
835 array-rows-numbered: A logical variable describing whether to ignore
b1d6ae0b
JB
836 row numbers in the buffer.
837
838Variables which are calculated:
41674a5a
DL
839 array-line-length: The number of characters in a buffer line.
840 array-lines-per-row: The number of buffer lines used to display each row.
b1d6ae0b
JB
841
842 The following commands are available (an asterisk indicates it may
843take a numeric prefix argument):
844
845 * \\<array-mode-map>\\[array-forward-column] Move forward one column.
846 * \\[array-backward-column] Move backward one column.
847 * \\[array-next-row] Move down one row.
848 * \\[array-previous-row] Move up one row.
849
850 * \\[array-copy-forward] Copy the current field into the column to the right.
851 * \\[array-copy-backward] Copy the current field into the column to the left.
852 * \\[array-copy-down] Copy the current field into the row below.
853 * \\[array-copy-up] Copy the current field into the row above.
854
855 * \\[array-copy-column-forward] Copy the current column into the column to the right.
856 * \\[array-copy-column-backward] Copy the current column into the column to the left.
857 * \\[array-copy-row-down] Copy the current row into the row below.
858 * \\[array-copy-row-up] Copy the current row into the row above.
859
860 \\[array-fill-rectangle] Copy the field at mark into every cell with row and column
861 between that of point and mark.
862
863 \\[array-what-position] Display the current array row and column.
864 \\[array-goto-cell] Go to a particular array cell.
865
866 \\[array-make-template] Make a template for a new array.
867 \\[array-reconfigure-rows] Reconfigure the array.
868 \\[array-expand-rows] Expand the array (remove row numbers and
869 newlines inside rows)
870
871 \\[array-display-local-variables] Display the current values of local variables.
872
873Entering array mode calls the function `array-mode-hook'."
874
875 (interactive)
e3e4b1f2 876 (kill-all-local-variables)
b1d6ae0b 877 ;; Number of rows in the array.
41674a5a 878 (make-local-variable 'array-max-row)
b1d6ae0b 879 ;; Number of columns in the array.
41674a5a 880 (make-local-variable 'array-max-column)
b1d6ae0b 881 ;; Number of array columns per line.
41674a5a 882 (make-local-variable 'array-columns-per-line)
b1d6ae0b 883 ;; Width of a field in the array.
41674a5a 884 (make-local-variable 'array-field-width)
b1d6ae0b 885 ;; Are rows numbered in the buffer?
41674a5a 886 (make-local-variable 'array-rows-numbered)
b1d6ae0b 887 ;; Length of a line in the array.
41674a5a 888 (make-local-variable 'array-line-length)
b1d6ae0b 889 ;; Number of lines per array row.
41674a5a 890 (make-local-variable 'array-lines-per-row)
b1d6ae0b 891 ;; Current line number of point in the buffer.
41674a5a 892 (make-local-variable 'array-buffer-line)
b1d6ae0b 893 ;; Current column number of point in the buffer.
41674a5a 894 (make-local-variable 'array-buffer-column)
b1d6ae0b
JB
895 ;; Current array row location of point.
896 (make-local-variable 'array-row)
897 ;; Current array column location of point.
898 (make-local-variable 'array-column)
899 ;; Current field string being copied.
41674a5a 900 (make-local-variable 'array-copy-string)
b1d6ae0b 901 ;; Should TAB conversion be prevented?
41674a5a
DL
902 (make-local-variable 'array-respect-tabs)
903 (setq array-respect-tabs nil)
b1d6ae0b
JB
904 (array-init-local-variables)
905 (setq major-mode 'array-mode)
906 (setq mode-name "Array")
a838308c 907 (force-mode-line-update)
7fbc46a0 908 (make-local-variable 'truncate-lines)
b1d6ae0b 909 (setq truncate-lines t)
5e5e5d42 910 (setq overwrite-mode 'overwrite-mode-textual)
b1d6ae0b 911 (use-local-map array-mode-map)
e3e4b1f2 912 (run-mode-hooks 'array-mode-hook))
b1d6ae0b
JB
913
914\f
915
916;;; Initialization functions. These are not interactive.
917
918(defun array-init-local-variables ()
41674a5a 919 "Initialize the variables associated with the array in this buffer."
b1d6ae0b
JB
920 (array-init-max-row)
921 (array-init-max-column)
922 (array-init-columns-per-line)
923 (array-init-field-width)
924 (array-init-rows-numbered)
925 (array-init-line-length)
926 (array-init-lines-per-row)
927 (message ""))
928
929(defun array-init-max-row (&optional arg)
41674a5a
DL
930 "Initialize the value of `array-max-row'."
931 (setq array-max-row
3981e5b5 932 (or arg (string-to-number (read-string "Number of array rows: ")))))
b1d6ae0b
JB
933
934(defun array-init-max-column (&optional arg)
41674a5a
DL
935 "Initialize the value of `array-max-column'."
936 (setq array-max-column
3981e5b5 937 (or arg (string-to-number (read-string "Number of array columns: ")))))
b1d6ae0b
JB
938
939(defun array-init-columns-per-line (&optional arg)
41674a5a
DL
940 "Initialize the value of `array-columns-per-line'."
941 (setq array-columns-per-line
3981e5b5 942 (or arg (string-to-number (read-string "Array columns per line: ")))))
b1d6ae0b
JB
943
944(defun array-init-field-width (&optional arg)
41674a5a
DL
945 "Initialize the value of `array-field-width'."
946 (setq array-field-width
3981e5b5 947 (or arg (string-to-number (read-string "Field width: ")))))
b1d6ae0b
JB
948
949(defun array-init-rows-numbered (&optional arg)
41674a5a
DL
950 "Initialize the value of `array-rows-numbered'."
951 (setq array-rows-numbered
b1d6ae0b
JB
952 (or arg (y-or-n-p "Rows numbered? "))))
953
954(defun array-init-line-length (&optional arg)
41674a5a
DL
955 "Initialize the value of `array-line-length'."
956 (setq array-line-length
b1d6ae0b 957 (or arg
41674a5a 958 (* array-field-width array-columns-per-line))))
b1d6ae0b
JB
959
960(defun array-init-lines-per-row (&optional arg)
41674a5a
DL
961 "Initialize the value of `array-lines-per-row'."
962 (setq array-lines-per-row
b1d6ae0b 963 (or arg
41674a5a
DL
964 (+ (floor (1- array-max-column) array-columns-per-line)
965 (if array-rows-numbered 2 1)))))
c0274f38 966
bcc7f26d
KH
967(provide 'array)
968
ab5796a9 969;;; arch-tag: 0086605d-79fe-4a1a-992a-456417261f80
c0274f38 970;;; array.el ends here