(c-after-change-check-<>-operators):
[bpt/emacs.git] / lisp / array.el
... / ...
CommitLineData
1;;; array.el --- array editing commands for GNU Emacs
2
3;; Copyright (C) 1987, 2000, 2002, 2003, 2004,
4;; 2005 Free Software Foundation, Inc.
5
6;; Author David M. Brown
7;; Maintainer: FSF
8;; Keywords: extensions
9
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
14;; the Free Software Foundation; either version 2, or (at your option)
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
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25;; Boston, MA 02110-1301, USA.
26
27;;; Commentary:
28
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.
41
42\f
43;;; Code:
44
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
61;;; Internal information functions.
62
63(defun array-cursor-in-array-range ()
64 "Return t if the cursor is in a valid array cell.
65Its ok to be on a row number line."
66 (let ((columns-last-line (% array-max-column array-columns-per-line)))
67 ;; Requires array-buffer-line and array-buffer-column to be current.
68 (not (or
69 ;; The cursor is too far to the right.
70 (>= array-buffer-column array-line-length)
71 ;; The cursor is below the last row.
72 (>= array-buffer-line (* array-lines-per-row array-max-row))
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.
76 (and (zerop (% (1+ array-buffer-line) array-lines-per-row))
77 (not (zerop columns-last-line))
78 (>= array-buffer-column (* columns-last-line array-field-width)))))))
79
80(defun array-current-row ()
81 "Return the array row of the field in which the cursor is located."
82 ;; Requires array-buffer-line and array-buffer-column to be current.
83 (and (array-cursor-in-array-range)
84 (1+ (floor array-buffer-line array-lines-per-row))))
85
86(defun array-current-column ()
87 "Return the array column of the field in which the cursor is located."
88 ;; Requires array-buffer-line and array-buffer-column to be current.
89 (and (array-cursor-in-array-range)
90 ;; It's not okay to be on a row number line.
91 (not (and array-rows-numbered
92 (zerop (% array-buffer-line array-lines-per-row))))
93 (+
94 ;; Array columns due to line differences.
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)))
99 ;; Array columns on the current line.
100 (1+ (floor array-buffer-column array-field-width)))))
101
102(defun array-update-array-position (&optional a-row a-column)
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.
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 ()
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)))
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)
121 (let ((array-buffer-line (current-line))
122 (array-buffer-column (current-column)))
123 (message "Array row: %s Array column: %s"
124 (prin1-to-string (array-current-row))
125 (prin1-to-string (array-current-column)))))
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"
136 (prin1-to-string array-max-row)))
137 (princ (format " max-column: %s\n"
138 (prin1-to-string array-max-column)))
139 (princ (format " columns-per-line: %s\n"
140 (prin1-to-string array-columns-per-line)))
141 (princ (format " field-width: %s\n"
142 (prin1-to-string array-field-width)))
143 (princ (format " rows-numbered: %s\n"
144 (prin1-to-string array-rows-numbered)))
145 (princ (format " lines-per-row: %s\n"
146 (prin1-to-string array-lines-per-row)))
147 (princ (format " line-length: %s\n"
148 (prin1-to-string array-line-length))))))
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."
157 ;; Requires that array-buffer-column be current.
158 (let ((goal-column (- array-buffer-column (% array-buffer-column array-field-width))))
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."
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)))
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)
174 "Move to array row A-ROW and array column A-COLUMN.
175Leave point at the beginning of the field and return the new buffer column."
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))))
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."
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))
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."
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))
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."
211 ;; Requires that array-buffer-line and array-buffer-column be current.
212 (let ((goal-column (array-beginning-of-field))
213 (array-row (or (array-current-row)
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"))
217 ((and (= array-row 1) (= sign -1))
218 (error "Beginning of array"))
219 (t
220 (progn
221 (forward-line (* sign array-lines-per-row))
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."
229 ;; Requires that array-buffer-line and array-buffer-column be current.
230 (let ((array-column (or (array-current-column)
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"))
234 ((and (= array-column 1) (= sign -1))
235 (error "Beginning of array"))
236 (t
237 (cond
238 ;; Going backward from first column on the line.
239 ((and (= sign -1) (= 1 (% array-column array-columns-per-line)))
240 (forward-line -1)
241 (move-to-column-untabify
242 (* array-field-width (1- array-columns-per-line))))
243 ;; Going forward from last column on the line.
244 ((and (= sign 1) (zerop (% array-column array-columns-per-line)))
245 (forward-line 1))
246 ;; Somewhere in the middle of the line.
247 (t
248 (move-to-column-untabify (+ (array-beginning-of-field)
249 (* array-field-width sign)))))
250 (+ array-column sign)))))
251
252(defun array-normalize-cursor ()
253 "Move the cursor to the first non-whitespace character in the field.
254If necessary, scroll horizontally to keep the cursor in view."
255 ;; Assumes point is at the beginning of the field.
256 (let ((array-buffer-column (current-column)))
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
264 ;; array-buffer-column will always be current.
265 (let ((w-hscroll (window-hscroll))
266 (w-width (window-width)))
267 (cond
268 ((and (>= array-buffer-column w-hscroll)
269 (<= array-buffer-column (+ w-hscroll w-width)))
270 ;; It's already visible. Do nothing.
271 nil)
272 ((> array-buffer-column (+ w-hscroll w-width))
273 ;; It's to the right. Scroll left.
274 (scroll-left (- (- array-buffer-column w-hscroll)
275 (/ w-width 2))))
276 (t
277 ;; It's to the left. Scroll right.
278 (scroll-right (+ (- w-hscroll array-buffer-column)
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")
289 (let ((array-buffer-line (current-line))
290 (array-buffer-column (current-column)))
291 (if (= (abs arg) 1)
292 (array-move-one-row arg)
293 (array-move-to-row
294 (limit-index (+ (or (array-current-row)
295 (error "Cursor is not in an array cell"))
296 arg)
297 array-max-row))))
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")
311 (let ((array-buffer-line (current-line))
312 (array-buffer-column (current-column)))
313 (if (= (abs arg) 1)
314 (array-move-one-column arg)
315 (array-move-to-column
316 (limit-index (+ (or (array-current-column)
317 (error "Cursor is not in an array cell"))
318 arg)
319 array-max-column))))
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
333 (limit-index a-row array-max-row)
334 (limit-index a-column array-max-column))
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."
343 ;; Requires that array-buffer-column be current.
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."
352 ;; Requires that array-buffer-line, array-buffer-column, and array-copy-string be current.
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)))
356 (insert array-copy-string))
357 (move-to-column array-buffer-column)
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."
364 ;; Requires that array-buffer-line, array-buffer-column, and array-copy-string be current.
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)))
369 (insert array-copy-string))
370 (move-to-column array-buffer-column)
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."
376 ;; Requires that array-buffer-line, array-buffer-column, array-row, and
377 ;; array-copy-string be current.
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)))
386 (insert array-copy-string))
387 (move-to-column array-buffer-column)
388 (setq count (1- count)))))
389
390(defun array-copy-to-column (a-column)
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.
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)))
403 (insert array-copy-string))
404 (move-to-column array-buffer-column)
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."
410 ;; Requires that array-copy-string be current.
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)))
414 (insert array-copy-string)
415 (move-to-column array-buffer-column))
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")
425 (let* ((array-buffer-line (current-line))
426 (array-buffer-column (current-column))
427 (array-row (or (array-current-row)
428 (error "Cursor is not in a valid array cell")))
429 (array-copy-string (array-field-string)))
430 (if (= (abs arg) 1)
431 (array-copy-once-vertically arg)
432 (array-copy-to-row
433 (limit-index (+ array-row arg) array-max-row))))
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")
446 (let* ((array-buffer-line (current-line))
447 (array-buffer-column (current-column))
448 (array-column (or (array-current-column)
449 (error "Cursor is not in a valid array cell")))
450 (array-copy-string (array-field-string)))
451 (if (= (abs arg) 1)
452 (array-copy-once-horizontally arg)
453 (array-copy-to-column
454 (limit-index (+ array-column arg) array-max-column))))
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)
470 (error "Cursor is not in a valid array cell"))
471 (message "Working...")
472 (let ((this-row 0))
473 (while (< this-row array-max-row)
474 (setq this-row (1+ this-row))
475 (array-move-to-cell this-row array-column)
476 (array-update-buffer-position)
477 (let ((array-copy-string (array-field-string)))
478 (if (= (abs arg) 1)
479 (array-copy-once-horizontally arg)
480 (array-copy-to-column
481 (limit-index (+ array-column arg) array-max-column))))))
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)
499 (error "Cursor is not in a valid array cell"))
500 (cond
501 ((and (= array-row 1) (= arg -1))
502 (error "Beginning of array"))
503 ((and (= array-row array-max-row) (= arg 1))
504 (error "End of array"))
505 (t
506 (let* ((array-copy-string
507 (buffer-substring
508 (save-excursion (array-move-to-cell array-row 1)
509 (point))
510 (save-excursion (array-move-to-cell array-row array-max-column)
511 (forward-line 1)
512 (point))))
513 (this-row array-row)
514 (goal-row (limit-index (+ this-row arg) array-max-row))
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
524 (array-move-to-cell this-row array-max-column)
525 (forward-line 1)
526 (point)))
527 (insert array-copy-string))
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)
544 (error "Cursor is not in a valid array cell")))
545 (p-column (or (array-current-column)
546 (error "Cursor is not in a valid array cell")))
547 (m-row
548 (save-excursion
549 (exchange-point-and-mark)
550 (array-update-buffer-position)
551 (or (array-current-row)
552 (error "Mark is not in a valid array cell"))))
553 (m-column
554 (save-excursion
555 (exchange-point-and-mark)
556 (array-update-buffer-position)
557 (or (array-current-column)
558 (error "Mark is not in a valid array cell")))))
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.
565 (let ((array-copy-string
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)
576 (let ((array-copy-string
577 (buffer-substring
578 (point)
579 (save-excursion
580 (array-move-to-cell top-row right-column)
581 (setq array-buffer-column (current-column))
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)
593 (setq array-buffer-column (current-column))
594 (array-end-of-field t)
595 (point)))
596 (insert array-copy-string)))))
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)
607 ;; If there is a conflict between array-field-width and init-string, resolve it.
608 (let ((check t)
609 (len))
610 (while check
611 (setq array-init-field (read-string "Initial field value: "))
612 (setq len (length array-init-field))
613 (if (/= len array-field-width)
614 (if (y-or-n-p (format "Change field width to %d? " len))
615 (progn (setq array-field-width len)
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.
622 (while (<= this-row array-max-row)
623 (if array-rows-numbered
624 (insert (format "%d:\n" this-row)))
625 (let ((this-column 1))
626 ;; Loop through the columns.
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))
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)
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
641NEW-ROWS-NUMBERED (a character, either ?y or ?n) is the desired value
642of `array-rows-numbered'."
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)
648 (<= new-columns-per-line array-max-column))
649 (setq check nil)
650 (setq new-columns-per-line
651 (string-to-number
652 (read-string
653 (format "Columns per line (1 - %d): " array-max-column)))))))
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)))
666 (temp-buffer (generate-new-buffer " *Array*"))
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)
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.
692 (insert (format "%d:\n" this-row))))
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)
721 (insert-buffer-substring temp-buffer)
722 ;; Update local variables.
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
727 (+ (floor (1- temp-max-column) new-columns-per-line)
728 (if new-rows-numbered 2 1)))
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)
736 (array-reconfigure-rows array-max-column ?n))
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
747(defun xor (pred1 pred2)
748 "Return the logical exclusive or of predicates PRED1 and PRED2."
749 (and (or pred1 pred2)
750 (not (and pred1 pred2))))
751
752(defun current-line ()
753 "Return the current buffer line at point. The first line is 0."
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.
764 (if array-respect-tabs
765 (error "There is a TAB character in the way")
766 (progn
767 (untabify-backward)
768 (move-to-column column)))))
769
770(defun untabify-backward ()
771 "Untabify the preceding TAB."
772 (save-excursion
773 (let ((start (point)))
774 (backward-char 1)
775 (untabify (point) start))))
776
777\f
778
779;;; Array mode.
780
781(defvar array-mode-map nil
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
810;;;###autoload
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
818 The array MUST reside at the top of the buffer.
819
820 TABs are not respected, and may be converted into spaces at any time.
821Setting the variable `array-respect-tabs' to non-nil will prevent TAB conversion,
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
826supply. These variables are all local to the buffer. Other buffer
827in array mode may have different values assigned to the variables.
828The variables are:
829
830Variables you assign:
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
836 row numbers in the buffer.
837
838Variables which are calculated:
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.
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)
876 (kill-all-local-variables)
877 ;; Number of rows in the array.
878 (make-local-variable 'array-max-row)
879 ;; Number of columns in the array.
880 (make-local-variable 'array-max-column)
881 ;; Number of array columns per line.
882 (make-local-variable 'array-columns-per-line)
883 ;; Width of a field in the array.
884 (make-local-variable 'array-field-width)
885 ;; Are rows numbered in the buffer?
886 (make-local-variable 'array-rows-numbered)
887 ;; Length of a line in the array.
888 (make-local-variable 'array-line-length)
889 ;; Number of lines per array row.
890 (make-local-variable 'array-lines-per-row)
891 ;; Current line number of point in the buffer.
892 (make-local-variable 'array-buffer-line)
893 ;; Current column number of point in the buffer.
894 (make-local-variable 'array-buffer-column)
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.
900 (make-local-variable 'array-copy-string)
901 ;; Should TAB conversion be prevented?
902 (make-local-variable 'array-respect-tabs)
903 (setq array-respect-tabs nil)
904 (array-init-local-variables)
905 (setq major-mode 'array-mode)
906 (setq mode-name "Array")
907 (force-mode-line-update)
908 (make-local-variable 'truncate-lines)
909 (setq truncate-lines t)
910 (setq overwrite-mode 'overwrite-mode-textual)
911 (use-local-map array-mode-map)
912 (run-mode-hooks 'array-mode-hook))
913
914\f
915
916;;; Initialization functions. These are not interactive.
917
918(defun array-init-local-variables ()
919 "Initialize the variables associated with the array in this buffer."
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)
930 "Initialize the value of `array-max-row'."
931 (setq array-max-row
932 (or arg (string-to-number (read-string "Number of array rows: ")))))
933
934(defun array-init-max-column (&optional arg)
935 "Initialize the value of `array-max-column'."
936 (setq array-max-column
937 (or arg (string-to-number (read-string "Number of array columns: ")))))
938
939(defun array-init-columns-per-line (&optional arg)
940 "Initialize the value of `array-columns-per-line'."
941 (setq array-columns-per-line
942 (or arg (string-to-number (read-string "Array columns per line: ")))))
943
944(defun array-init-field-width (&optional arg)
945 "Initialize the value of `array-field-width'."
946 (setq array-field-width
947 (or arg (string-to-number (read-string "Field width: ")))))
948
949(defun array-init-rows-numbered (&optional arg)
950 "Initialize the value of `array-rows-numbered'."
951 (setq array-rows-numbered
952 (or arg (y-or-n-p "Rows numbered? "))))
953
954(defun array-init-line-length (&optional arg)
955 "Initialize the value of `array-line-length'."
956 (setq array-line-length
957 (or arg
958 (* array-field-width array-columns-per-line))))
959
960(defun array-init-lines-per-row (&optional arg)
961 "Initialize the value of `array-lines-per-row'."
962 (setq array-lines-per-row
963 (or arg
964 (+ (floor (1- array-max-column) array-columns-per-line)
965 (if array-rows-numbered 2 1)))))
966
967(provide 'array)
968
969;;; arch-tag: 0086605d-79fe-4a1a-992a-456417261f80
970;;; array.el ends here