|
|
1.1 root 1: ;; "Picture mode" -- editing using quarter-plane screen model.
2: ;; Copyright (C) 1985 Free Software Foundation, Inc.
3: ;; Principal author K. Shane Hartman
4:
5: ;; This file is part of GNU Emacs.
6:
7: ;; GNU Emacs is distributed in the hope that it will be useful,
8: ;; but WITHOUT ANY WARRANTY. No author or distributor
9: ;; accepts responsibility to anyone for the consequences of using it
10: ;; or for whether it serves any particular purpose or works at all,
11: ;; unless he says so in writing. Refer to the GNU Emacs General Public
12: ;; License for full details.
13:
14: ;; Everyone is granted permission to copy, modify and redistribute
15: ;; GNU Emacs, but only under the conditions described in the
16: ;; GNU Emacs General Public License. A copy of this license is
17: ;; supposed to have been given to you along with GNU Emacs so you
18: ;; can know your rights and responsibilities. It should be in a
19: ;; file named COPYING. Among other things, the copyright notice
20: ;; and this notice must be preserved on all copies.
21:
22:
23: (provide 'picture)
24:
25: (defun move-to-column-force (column)
26: "Move to column COLUMN in current line.
27: Differs from move-to-column in that it creates or modifies whitespace
28: if necessary to attain exactly the specified column."
29: (move-to-column column)
30: (let ((col (current-column)))
31: (if (< col column)
32: (indent-to column)
33: (if (and (/= col column)
34: (= (preceding-char) ?\t))
35: (let (indent-tabs-mode)
36: (delete-char -1)
37: (indent-to col)
38: (move-to-column column))))))
39:
40:
41: ;; Picture Movement Commands
42:
43: (defun picture-end-of-line (&optional arg)
44: "Position point after last non-blank character on current line.
45: With ARG not nil, move forward ARG - 1 lines first.
46: If scan reaches end of buffer, stop there without error."
47: (interactive "P")
48: (if arg (forward-line (1- (prefix-numeric-value arg))))
49: (beginning-of-line)
50: (skip-chars-backward " \t" (prog1 (point) (end-of-line))))
51:
52: (defun picture-forward-column (arg)
53: "Move cursor right, making whitespace if necessary.
54: With argument, move that many columns."
55: (interactive "p")
56: (move-to-column-force (+ (current-column) arg)))
57:
58: (defun picture-backward-column (arg)
59: "Move cursor left, making whitespace if necessary.
60: With argument, move that many columns."
61: (interactive "p")
62: (move-to-column-force (- (current-column) arg)))
63:
64: (defun picture-move-down (arg)
65: "Move vertically down, making whitespace if necessary.
66: With argument, move that many lines."
67: (interactive "p")
68: (let ((col (current-column)))
69: (picture-newline arg)
70: (move-to-column-force col)))
71:
72: (defconst picture-vertical-step 0
73: "Amount to move vertically after text character in Picture mode.")
74:
75: (defconst picture-horizontal-step 1
76: "Amount to move horizontally after text character in Picture mode.")
77:
78: (defun picture-move-up (arg)
79: "Move vertically up, making whitespace if necessary.
80: With argument, move that many lines."
81: (interactive "p")
82: (picture-move-down (- arg)))
83:
84: (defun picture-movement-right ()
85: "Move right after self-inserting character in Picture mode."
86: (interactive)
87: (picture-set-motion 0 1))
88:
89: (defun picture-movement-left ()
90: "Move left after self-inserting character in Picture mode."
91: (interactive)
92: (picture-set-motion 0 -1))
93:
94: (defun picture-movement-up ()
95: "Move up after self-inserting character in Picture mode."
96: (interactive)
97: (picture-set-motion -1 0))
98:
99: (defun picture-movement-down ()
100: "Move down after self-inserting character in Picture mode."
101: (interactive)
102: (picture-set-motion 1 0))
103:
104: (defun picture-movement-nw ()
105: "Move up and left after self-inserting character in Picture mode."
106: (interactive)
107: (picture-set-motion -1 -1))
108:
109: (defun picture-movement-ne ()
110: "Move up and right after self-inserting character in Picture mode."
111: (interactive)
112: (picture-set-motion -1 1))
113:
114: (defun picture-movement-sw ()
115: "Move down and left after self-inserting character in Picture mode."
116: (interactive)
117: (picture-set-motion 1 -1))
118:
119: (defun picture-movement-se ()
120: "Move down and right after self-inserting character in Picture mode."
121: (interactive)
122: (picture-set-motion 1 1))
123:
124: (defun picture-set-motion (vert horiz)
125: "Set VERTICAL and HORIZONTAL increments for movement in Picture mode.
126: The mode line is updated to reflect the current direction."
127: (setq picture-vertical-step vert
128: picture-horizontal-step horiz)
129: (setq mode-name
130: (format "Picture:%s"
131: (car (nthcdr (+ 1 (% horiz 2) (* 3 (1+ (% vert 2))))
132: '(nw up ne left none right sw down se)))))
133: ;; Kludge - force the mode line to be updated. Is there a better
134: ;; way to this?
135: (set-buffer-modified-p (buffer-modified-p))
136: (message ""))
137:
138: (defun picture-move ()
139: "Move in direction of picture-vertical-step and picture-horizontal-step."
140: (picture-move-down picture-vertical-step)
141: (picture-forward-column picture-horizontal-step))
142:
143: (defun picture-motion (arg)
144: "Move point in direction of current picture motion in Picture mode.
145: With ARG do it that many times. Useful for delineating rectangles in
146: conjunction with diagonal picture motion.
147: Do \\[command-apropos] picture-movement to see commands which control motion."
148: (interactive "p")
149: (picture-move-down (* arg picture-vertical-step))
150: (picture-forward-column (* arg picture-horizontal-step)))
151:
152: (defun picture-motion-reverse (arg)
153: "Move point in direction opposite of current picture motion in Picture mode.
154: With ARG do it that many times. Useful for delineating rectangles in
155: conjunction with diagonal picture motion.
156: Do \\[command-apropos] picture-movement to see commands which control motion."
157: (interactive "p")
158: (picture-motion (- arg)))
159:
160:
161: ;; Picture insertion and deletion.
162:
163: (defun picture-self-insert (arg)
164: "Insert this character in place of character previously at the cursor.
165: The cursor then moves in the direction you previously specified
166: with the commands picture-movement-right, picture-movement-up, etc.
167: Do \\[command-apropos] picture-movement to see those commands."
168: (interactive "p")
169: (while (> arg 0)
170: (setq arg (1- arg))
171: (move-to-column-force (1+ (current-column)))
172: (delete-char -1)
173: (insert last-input-char)
174: (forward-char -1)
175: (picture-move)))
176:
177: (defun picture-clear-column (arg)
178: "Clear out ARG columns after point without moving."
179: (interactive "p")
180: (let* ((opoint (point))
181: (original-col (current-column))
182: (target-col (+ original-col arg)))
183: (move-to-column-force target-col)
184: (delete-region opoint (point))
185: (save-excursion
186: (indent-to (max target-col original-col)))))
187:
188: (defun picture-backward-clear-column (arg)
189: "Clear out ARG columns before point, moving back over them."
190: (interactive "p")
191: (picture-clear-column (- arg)))
192:
193: (defun picture-clear-line (arg)
194: "Clear out rest of line; if at end of line, advance to next line.
195: Cleared-out line text goes into the kill ring, as do
196: newlines that are advanced over.
197: With argument, clear out (and save in kill ring) that many lines."
198: (interactive "P")
199: (if arg
200: (progn
201: (setq arg (prefix-numeric-value arg))
202: (kill-line arg)
203: (newline (if (> arg 0) arg (- arg))))
204: (if (looking-at "[ \t]*$")
205: (kill-ring-save (point) (progn (forward-line 1) (point)))
206: (kill-region (point) (progn (end-of-line) (point))))))
207:
208: (defun picture-newline (arg)
209: "Move to the beginning of the following line.
210: With argument, moves that many lines (up, if negative argument);
211: always moves to the beginning of a line."
212: (interactive "p")
213: (if (< arg 0)
214: (forward-line arg)
215: (while (> arg 0)
216: (end-of-line)
217: (if (eobp) (newline) (forward-char 1))
218: (setq arg (1- arg)))))
219:
220: (defun picture-open-line (arg)
221: "Insert an empty line after the current line.
222: With positive argument insert that many lines."
223: (interactive "p")
224: (save-excursion
225: (end-of-line)
226: (open-line arg)))
227:
228: (defun picture-duplicate-line ()
229: "Insert a duplicate of the current line, below it."
230: (interactive)
231: (save-excursion
232: (let ((contents
233: (buffer-substring
234: (progn (beginning-of-line) (point))
235: (progn (picture-newline 1) (point)))))
236: (forward-line -1)
237: (insert contents))))
238:
239:
240: ;; Picture Tabs
241:
242: (defvar picture-tab-chars "!-~"
243: "*A character set which controls behavior of commands
244: \\[picture-set-tab-stops] and \\[picture-tab-search]. It is NOT a
245: regular expression, any regexp special characters will be quoted.
246: It defines a set of \"interesting characters\" to look for when setting
247: \(or searching for) tab stops, initially \"!-~\" (all printing characters).
248: For example, suppose that you are editing a table which is formatted thus:
249: | foo | bar + baz | 23 *
250: | bubbles | and + etc | 97 *
251: and that picture-tab-chars is \"|+*\". Then invoking
252: \\[picture-set-tab-stops] on either of the previous lines would result
253: in the following tab stops
254: : : : :
255: Another example - \"A-Za-z0-9\" would produce the tab stops
256: : : : :
257:
258: Note that if you want the character `-' to be in the set, it must be
259: included in a range or else appear in a context where it cannot be
260: taken for indicating a range (e.g. \"-A-Z\" declares the set to be the
261: letters `A' through `Z' and the character `-'). If you want the
262: character `\\' in the set it must be preceded by itself: \"\\\\\".
263:
264: The command \\[picture-tab-search] is defined to move beneath (or to) a
265: character belonging to this set independent of the tab stops list.")
266:
267: (defun picture-set-tab-stops (&optional arg)
268: "Set value of tab-stop-list according to context of this line.
269: This controls the behavior of \\[picture-tab]. A tab stop
270: is set at every column occupied by an \"interesting character\" that is
271: preceded by whitespace. Interesting characters are defined by the
272: variable picture-tab-chars, see its documentation for an example
273: of usage. With ARG, just (re)set tab-stop-list to its default value.
274: The tab stops computed are displayed in the minibuffer with `:' at
275: each stop."
276: (interactive "P")
277: (save-excursion
278: (let (tabs)
279: (if arg
280: (setq tabs (default-value 'tab-stop-list))
281: (let ((regexp (concat "[ \t]+[" (regexp-quote picture-tab-chars) "]")))
282: (beginning-of-line)
283: (let ((bol (point)))
284: (end-of-line)
285: (while (re-search-backward regexp bol t)
286: (skip-chars-forward " \t")
287: (setq tabs (cons (current-column) tabs)))
288: (if (null tabs)
289: (error "No characters in set %s on this line."
290: (regexp-quote picture-tab-chars))))))
291: (setq tab-stop-list tabs)
292: (let ((blurb (make-string (1+ (nth (1- (length tabs)) tabs)) ?\ )))
293: (while tabs
294: (aset blurb (car tabs) ?:)
295: (setq tabs (cdr tabs)))
296: (message blurb)))))
297:
298: (defun picture-tab-search (&optional arg)
299: "Move to column beneath next interesting char in previous line.
300: With ARG move to column occupied by next interesting character in this
301: line. The character must be preceded by whitespace.
302: \"interesting characters\" are defined by variable picture-tab-chars.
303: If no such character is found, move to beginning of line."
304: (interactive "P")
305: (let ((target (current-column)))
306: (save-excursion
307: (if (and (not arg)
308: (progn
309: (beginning-of-line)
310: (skip-chars-backward
311: (concat "^" (regexp-quote picture-tab-chars))
312: (point-min))
313: (not (bobp))))
314: (move-to-column target))
315: (if (re-search-forward
316: (concat "[ \t]+[" (regexp-quote picture-tab-chars) "]")
317: (save-excursion (end-of-line) (point))
318: 'move)
319: (setq target (1- (current-column)))
320: (setq target nil)))
321: (if target
322: (move-to-column-force target)
323: (beginning-of-line))))
324:
325: (defun picture-tab (&optional arg)
326: "Tab transparently (move) to next tab stop.
327: With ARG overwrite the traversed text with spaces. The tab stop
328: list can be changed by \\[picture-set-tab-stops] and \\[edit-tab-stops].
329: See also documentation for variable picture-tab-chars."
330: (interactive "P")
331: (let* ((opoint (point))
332: (target (prog2 (tab-to-tab-stop)
333: (current-column)
334: (delete-region opoint (point)))))
335: (move-to-column-force target)
336: (if arg
337: (let (indent-tabs-mode)
338: (delete-region opoint (point))
339: (indent-to target)))))
340:
341: ;; Picture Rectangles
342:
343: (defconst picture-killed-rectangle nil
344: "Rectangle killed or copied by \\[picture-clear-rectangle] in Picture mode.
345: The contents can be retrieved by \\[picture-yank-rectangle]")
346:
347: (defun picture-clear-rectangle (start end &optional killp)
348: "Clear and save rectangle delineated by point and mark.
349: The rectangle is saved for yanking by \\[picture-yank-rectangle] and replaced
350: with whitespace. The previously saved rectangle, if any, is lost.
351: With prefix argument, the rectangle is actually killed, shifting remaining
352: text."
353: (interactive "r\nP")
354: (setq picture-killed-rectangle (picture-snarf-rectangle start end killp)))
355:
356: (defun picture-clear-rectangle-to-register (start end register &optional killp)
357: "Clear rectangle delineated by point and mark into REGISTER.
358: The rectangle is saved in REGISTER and replaced with whitespace.
359: With prefix argument, the rectangle is actually killed, shifting remaining
360: text."
361: (interactive "r\ncRectangle to register: \nP")
362: (set-register register (picture-snarf-rectangle start end killp)))
363:
364: (defun picture-snarf-rectangle (start end &optional killp)
365: (let ((column (current-column))
366: (indent-tabs-mode nil))
367: (prog1 (save-excursion
368: (if killp
369: (delete-extract-rectangle start end)
370: (prog1 (extract-rectangle start end)
371: (clear-rectangle start end))))
372: (move-to-column-force column))))
373:
374: (defun picture-yank-rectangle (&optional insertp)
375: "Overlay rectangle saved by \\[picture-clear-rectangle]
376: The rectangle is positioned with upper left corner at point, overwriting
377: existing text. With prefix argument, the rectangle is inserted instead,
378: shifting existing text. Leaves mark at one corner of rectangle and
379: point at the other (diagonally opposed) corner."
380: (interactive "P")
381: (if (not (consp picture-killed-rectangle))
382: (error "No rectangle saved.")
383: (picture-insert-rectangle picture-killed-rectangle insertp)))
384:
385: (defun picture-yank-rectangle-from-register (register &optional insertp)
386: "Overlay rectangle saved in REGISTER.
387: The rectangle is positioned with upper left corner at point, overwriting
388: existing text. With prefix argument, the rectangle is
389: inserted instead, shifting existing text. Leaves mark at one corner
390: of rectangle and point at the other (diagonally opposed) corner."
391: (interactive "cRectangle from register: \nP")
392: (let ((rectangle (get-register register)))
393: (if (not (consp rectangle))
394: (error "Register %c does not contain a rectangle." register)
395: (picture-insert-rectangle rectangle insertp))))
396:
397: (defun picture-insert-rectangle (rectangle &optional insertp)
398: "Overlay RECTANGLE with upper left corner at point.
399: Optional argument INSERTP, if non-nil causes RECTANGLE to be inserted.
400: Leaves the region surrounding the rectangle."
401: (let ((indent-tabs-mode nil))
402: (if (not insertp)
403: (save-excursion
404: (delete-rectangle (point)
405: (progn
406: (picture-forward-column (length (car rectangle)))
407: (picture-move-down (1- (length rectangle)))
408: (point)))))
409: (push-mark)
410: (insert-rectangle rectangle)))
411:
412:
413: ;; Picture Keymap, entry and exit points.
414:
415: (defconst picture-mode-map nil)
416:
417: (if (not picture-mode-map)
418: (let ((i ?\ ))
419: (setq picture-mode-map (make-keymap))
420: (while (< i ?\177)
421: (aset picture-mode-map i 'picture-self-insert)
422: (setq i (1+ i)))
423: (define-key picture-mode-map "\C-f" 'picture-forward-column)
424: (define-key picture-mode-map "\C-b" 'picture-backward-column)
425: (define-key picture-mode-map "\C-d" 'picture-clear-column)
426: (define-key picture-mode-map "\C-c\C-d" 'delete-char)
427: (define-key picture-mode-map "\177" 'picture-backward-clear-column)
428: (define-key picture-mode-map "\C-k" 'picture-clear-line)
429: (define-key picture-mode-map "\C-o" 'picture-open-line)
430: (define-key picture-mode-map "\C-m" 'picture-newline)
431: (define-key picture-mode-map "\C-j" 'picture-duplicate-line)
432: (define-key picture-mode-map "\C-n" 'picture-move-down)
433: (define-key picture-mode-map "\C-p" 'picture-move-up)
434: (define-key picture-mode-map "\C-e" 'picture-end-of-line)
435: (define-key picture-mode-map "\e\t" 'picture-toggle-tab-state)
436: (define-key picture-mode-map "\t" 'picture-tab)
437: (define-key picture-mode-map "\e\t" 'picture-tab-search)
438: (define-key picture-mode-map "\C-c\t" 'picture-set-tab-stops)
439: (define-key picture-mode-map "\C-c\C-k" 'picture-clear-rectangle)
440: (define-key picture-mode-map "\C-c\C-w" 'picture-clear-rectangle-to-register)
441: (define-key picture-mode-map "\C-c\C-y" 'picture-yank-rectangle)
442: (define-key picture-mode-map "\C-c\C-x" 'picture-yank-rectangle-from-register)
443: (define-key picture-mode-map "\C-c\C-c" 'picture-mode-exit)
444: (define-key picture-mode-map "\C-c\C-f" 'picture-motion)
445: (define-key picture-mode-map "\C-c\C-b" 'picture-motion-reverse)
446: (define-key picture-mode-map "\C-c<" 'picture-movement-left)
447: (define-key picture-mode-map "\C-c>" 'picture-movement-right)
448: (define-key picture-mode-map "\C-c^" 'picture-movement-up)
449: (define-key picture-mode-map "\C-c." 'picture-movement-down)
450: (define-key picture-mode-map "\C-c`" 'picture-movement-nw)
451: (define-key picture-mode-map "\C-c'" 'picture-movement-ne)
452: (define-key picture-mode-map "\C-c/" 'picture-movement-sw)
453: (define-key picture-mode-map "\C-c\\" 'picture-movement-se)))
454:
455: (defvar edit-picture-hook nil
456: "If non-nil, it's value is called on entry to Picture mode.
457: Picture mode is invoked by the command \\[edit-picture].")
458:
459: (defun edit-picture ()
460: "Switch to Picture mode, in which a quarter-plane screen model is used.
461: Printing characters replace instead of inserting themselves with motion
462: afterwards settable by these commands:
463: C-c < Move left after insertion.
464: C-c > Move right after insertion.
465: C-c ^ Move up after insertion.
466: C-c . Move down after insertion.
467: C-c ` Move northwest (nw) after insertion.
468: C-c ' Move northeast (ne) after insertion.
469: C-c / Move southwest (sw) after insertion.
470: C-c \\ Move southeast (se) after insertion.
471: The current direction is displayed in the mode line. The initial
472: direction is right. Whitespace is inserted and tabs are changed to
473: spaces when required by movement. You can move around in the buffer
474: with these commands:
475: C-p Move vertically to SAME column in previous line.
476: C-n Move vertically to SAME column in next line.
477: C-e Move to column following last non-whitespace character.
478: C-f Move right inserting spaces if required.
479: C-b Move left changing tabs to spaces if required.
480: C-c C-f Move in direction of current picture motion.
481: C-c C-b Move in opposite direction of current picture motion.
482: Return Move to beginning of next line.
483: You can edit tabular text with these commands:
484: M-Tab Move to column beneath (or at) next interesting character.
485: `Indents' relative to a previous line.
486: Tab Move to next stop in tab stop list.
487: C-c Tab Set tab stops according to context of this line.
488: With ARG resets tab stops to default (global) value.
489: See also documentation of variable picture-tab-chars
490: which defines \"interesting character\". You can manually
491: change the tab stop list with command \\[edit-tab-stops].
492: You can manipulate text with these commands:
493: C-d Clear (replace) ARG columns after point without moving.
494: C-c C-d Delete char at point - the command normally assigned to C-d.
495: Delete Clear (replace) ARG columns before point, moving back over them.
496: C-k Clear ARG lines, advancing over them. The cleared
497: text is saved in the kill ring.
498: C-o Open blank line(s) beneath current line.
499: You can manipulate rectangles with these commands:
500: C-c C-k Clear (or kill) a rectangle and save it.
501: C-c C-w Like C-c C-k except rectangle is saved in named register.
502: C-c C-y Overlay (or insert) currently saved rectangle at point.
503: C-c C-x Like C-c C-y except rectangle is taken from named register.
504: \\[copy-rectangle-to-register] Copies a rectangle to a register.
505: \\[advertised-undo] Can undo effects of rectangle overlay commands
506: commands if invoked soon enough.
507: You can return to the previous mode with:
508: C-c C-c Which also strips trailing whitespace from every line.
509: Stripping is suppressed by supplying an argument.
510:
511: Entry to this mode calls the value of edit-picture-hook if non-nil.
512:
513: Note that Picture mode commands will work outside of Picture mode, but
514: they are not defaultly assigned to keys."
515: (interactive)
516: (if (eq major-mode 'edit-picture)
517: (error "You are already editing a Picture.")
518: (make-local-variable 'picture-mode-old-local-map)
519: (setq picture-mode-old-local-map (current-local-map))
520: (use-local-map picture-mode-map)
521: (make-local-variable 'picture-mode-old-mode-name)
522: (setq picture-mode-old-mode-name mode-name)
523: (make-local-variable 'picture-mode-old-major-mode)
524: (setq picture-mode-old-major-mode major-mode)
525: (setq major-mode 'edit-picture)
526: (make-local-variable 'picture-killed-rectangle)
527: (setq picture-killed-rectangle nil)
528: (make-local-variable 'tab-stop-list)
529: (setq tab-stop-list (default-value 'tab-stop-list))
530: (make-local-variable 'picture-tab-chars)
531: (setq picture-tab-chars (default-value 'picture-tab-chars))
532: (make-local-variable 'picture-vertical-step)
533: (make-local-variable 'picture-horizontal-step)
534: (picture-set-motion 0 1)
535: (run-hooks 'edit-picture-hook)
536: (message
537: (substitute-command-keys
538: "Type \\[picture-mode-exit] in this buffer to return it to %s mode.")
539: picture-mode-old-mode-name)))
540:
541: (fset 'picture-mode 'edit-picture) ; for the confused
542:
543: (defun picture-mode-exit (&optional nostrip)
544: "Undo edit-picture and return to previous major mode.
545: With no argument strips whitespace from end of every line in Picture buffer
546: otherwise just return to previous mode."
547: (interactive "P")
548: (if (not (eq major-mode 'edit-picture))
549: (error "You aren't editing a Picture.")
550: (if (not nostrip) (picture-clean))
551: (setq mode-name picture-mode-old-mode-name)
552: (use-local-map picture-mode-old-local-map)
553: (setq major-mode picture-mode-old-major-mode)
554: (kill-local-variable 'tab-stop-list)
555: ;; Kludge - force the mode line to be updated. Is there a better
556: ;; way to do this?
557: (set-buffer-modified-p (buffer-modified-p))))
558:
559: (defun picture-clean ()
560: "Eliminate whitespace at ends of lines."
561: (save-excursion
562: (goto-char (point-min))
563: (while (re-search-forward "[ \t][ \t]*$" nil t)
564: (delete-region (match-beginning 0) (point)))))
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.