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