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