diff lisp/rect.el @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children 8de8e3f6228a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/rect.el	Mon Aug 13 11:28:15 2007 +0200
@@ -0,0 +1,330 @@
+;;; rect.el --- rectangle functions for XEmacs.
+
+;; Copyright (C) 1985, 1993, 1994, 1999 Free Software Foundation, Inc.
+
+;; Maintainer: Didier Verna <verna@inf.enst.fr>
+;; Keywords: internal
+
+;; This file is part of XEmacs.
+
+;; XEmacs is free software; you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; XEmacs is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;; General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with XEmacs; see the file COPYING.  If not, write to the Free
+;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+;; 02111-1307, USA.
+
+;;; Synched up with: to be incorporated in a forthcoming GNU Emacs
+
+;;; Commentary:
+
+;; This package provides the operations on rectangles that are documented
+;; in the XEmacs Reference Manual.
+
+;; ### NOTE: this file has been almost completely rewritten by Didier Verna
+;; <verna@inf.enst.fr>, Jul 99. The purpose of this rewrite is to be less
+;; intrusive and fill lines with whitespaces only when needed. A few functions
+;; are untouched though, as noted above their definition.
+
+
+;;; Code:
+
+;; ### NOTE: this function is untouched, but not used anymore.
+;; `apply-on-rectangle' is used instead. It's still there because it's
+;; documented so people might use it in their code, so I've decided not to
+;; touch it. --dv
+;; XEmacs: extra-args
+(defun operate-on-rectangle (function start end coerce-tabs &rest extra-args)
+  "Call FUNCTION for each line of rectangle with corners at START, END.
+If COERCE-TABS is non-nil, convert multi-column characters
+that span the starting or ending columns on any line
+to multiple spaces before calling FUNCTION.
+FUNCTION is called with three arguments:
+ position of start of segment of this line within the rectangle,
+ number of columns that belong to rectangle but are before that position,
+ number of columns that belong to rectangle but are after point.
+Point is at the end of the segment of this line within the rectangle."
+  (let (startcol startlinepos endcol endlinepos)
+    (save-excursion
+      (goto-char start)
+      (setq startcol (current-column))
+      (beginning-of-line)
+      (setq startlinepos (point)))
+    (save-excursion
+      (goto-char end)
+      (setq endcol (current-column))
+      (forward-line 1)
+      (setq endlinepos (point-marker)))
+    (if (< endcol startcol)
+	;; XEmacs
+	(let ((tem startcol))
+	  (setq startcol endcol endcol tem)))
+    (save-excursion
+      (goto-char startlinepos)
+      (while (< (point) endlinepos)
+	(let (startpos begextra endextra)
+	  (move-to-column startcol coerce-tabs)
+	  (setq begextra (- (current-column) startcol))
+	  (setq startpos (point))
+	  (move-to-column endcol coerce-tabs)
+	  (setq endextra (- endcol (current-column)))
+	  (if (< begextra 0)
+	      (setq endextra (+ endextra begextra)
+		    begextra 0))
+	  (if (< endextra 0) (setq endextra 0))
+	  (apply function startpos begextra endextra extra-args))
+	(forward-line 1)))
+    (- endcol startcol)))
+
+;; The replacement for `operate-on-rectangle' -- dv
+(defun apply-on-rectangle (function start end &rest args)
+  "Call FUNCTION for each line of rectangle with corners at START and END.
+FUNCTION is called with two arguments: the start and end columns of the
+rectangle, plus ARGS extra arguments. Point is at the beginning of line
+when the function is called."
+  (let (startcol startpt endcol endpt)
+    (save-excursion
+      (goto-char start)
+      (setq startcol (current-column))
+      (beginning-of-line)
+      (setq startpt (point))
+      (goto-char end)
+      (setq endcol (current-column))
+      (forward-line 1)
+      (setq endpt (point-marker))
+      ;; ensure the start column is the left one.
+      (if (< endcol startcol)
+	  (let ((col startcol))
+	    (setq startcol endcol endcol col)))
+      ;; start looping over lines
+      (goto-char startpt)
+      (while (< (point) endpt)
+	(apply function startcol endcol args)
+	(forward-line 1)))
+    ))
+
+;; I love ascii art ;-)
+(defconst spaces-strings '[""
+			   " "
+			   "  "
+			   "   "
+			   "    "
+			   "     "
+			   "      "
+			   "       "
+			   "        "])
+
+
+;; This function is untouched --dv
+(defun spaces-string (n)
+  (if (<= n 8) (aref spaces-strings n)
+    (let ((val ""))
+      (while (> n 8)
+	(setq val (concat "        " val)
+	      n (- n 8)))
+      (concat val (aref spaces-strings n)))))
+
+;;;###autoload
+(defvar killed-rectangle nil
+  "Rectangle for `yank-rectangle' to insert.")
+
+;;;###autoload
+(defun kill-rectangle (start end &optional fill)
+  "Delete the region-rectangle and save it as the last killed one.
+You might prefer to use `delete-extract-rectangle' from a program.
+
+When called from a program, the rectangle's corners are START and END.
+With a prefix (or FILL) argument, also fill lines where nothing has to be
+deleted."
+  (interactive "*r\nP")
+  (when buffer-read-only
+    (setq killed-rectangle (extract-rectangle start end))
+    (barf-if-buffer-read-only))
+  (setq killed-rectangle (delete-extract-rectangle start end fill)))
+
+;;;###autoload
+(defun delete-rectangle (start end &optional fill)
+  "Delete the text in the region-rectangle without saving it.
+The same range of columns is deleted in each line starting with the line
+where the region begins and ending with the line where the region ends.
+
+When called from a program, the rectangle's corners are START and END.
+With a prefix (or FILL) argument, also fill lines where nothing has to be
+deleted."
+  (interactive "*r\nP")
+  (apply-on-rectangle 'delete-rectangle-line start end fill))
+
+(defun delete-rectangle-line (startcol endcol fill)
+  (let ((pt (point-at-eol)))
+    (when (= (move-to-column startcol (or fill 'coerce)) startcol)
+      (if (and (not fill) (<= pt endcol))
+	  (delete-region (point) pt)
+	;; else
+	(setq pt (point))
+	(move-to-column endcol t)
+	(delete-region pt (point))))
+    ))
+
+;;;###autoload
+(defun delete-extract-rectangle (start end &optional fill)
+  "Delete the contents of the rectangle with corners at START and END, and
+return it as a list of strings, one for each line of the rectangle.
+
+With an optional FILL argument, also fill lines where nothing has to be
+deleted."
+  (let ((lines (list nil)))
+    (apply-on-rectangle 'delete-extract-rectangle-line start end lines fill)
+    (nreverse (cdr lines))))
+
+(defun delete-extract-rectangle-line (startcol endcol lines fill)
+  (let ((pt (point-at-eol)))
+    (if (< (move-to-column startcol (or fill 'coerce)) startcol)
+	(setcdr lines (cons (spaces-string (- endcol startcol))
+			    (cdr lines)))
+      ;; else
+      (setq pt (point))
+      (move-to-column endcol t)
+      (setcdr lines (cons (buffer-substring pt (point)) (cdr lines)))
+      (delete-region pt (point)))
+    ))
+
+;;;###autoload
+(defun extract-rectangle (start end)
+  "Return the contents of the rectangle with corners at START and END,
+as a list of strings, one for each line of the rectangle."
+  (let ((lines (list nil)))
+    (apply-on-rectangle 'extract-rectangle-line start end lines)
+    (nreverse (cdr lines))))
+
+;; ### NOTE: this is actually the only function that needs to do complicated
+;; stuff like what's happening in `operate-on-rectangle', because the buffer
+;; might be read-only. --dv
+(defun extract-rectangle-line (startcol endcol lines)
+  (let (start end begextra endextra line)
+    (move-to-column startcol)
+    (setq start (point)
+	  begextra (- (current-column) startcol))
+    (move-to-column endcol)
+    (setq end (point)
+	  endextra (- endcol (current-column)))
+    (setq line (buffer-substring start (point)))
+    (if (< begextra 0)
+	(setq endextra (+ endextra begextra)
+	      begextra 0))
+    (if (< endextra 0)
+	(setq endextra 0))
+    (goto-char start)
+    (while (search-forward "\t" end t)
+      (let ((width (- (current-column)
+		      (save-excursion (forward-char -1)
+				      (current-column)))))
+	(setq line (concat (substring line 0 (- (point) end 1))
+			   (spaces-string width)
+			   (substring line (+ (length line)
+					      (- (point) end)))))))
+    (if (or (> begextra 0) (> endextra 0))
+	(setq line (concat (spaces-string begextra)
+			   line
+			   (spaces-string endextra))))
+    (setcdr lines (cons line (cdr lines)))))
+
+;; This function is untouched --dv
+;;;###autoload
+(defun yank-rectangle ()
+  "Yank the last killed rectangle with upper left corner at point."
+  (interactive "*")
+  (insert-rectangle killed-rectangle))
+
+;; This function is untouched --dv
+;;;###autoload
+(defun insert-rectangle (rectangle)
+  "Insert text of RECTANGLE with upper left corner at point.
+RECTANGLE's first line is inserted at point, its second
+line is inserted at a point vertically under point, etc.
+RECTANGLE should be a list of strings.
+After this command, the mark is at the upper left corner
+and point is at the lower right corner."
+  (let ((lines rectangle)
+	(insertcolumn (current-column))
+	(first t))
+    (push-mark)
+    (while lines
+      (or first
+	  (progn
+	    (forward-line 1)
+	    (or (bolp) (insert ?\n))
+	    (move-to-column insertcolumn t)))
+      (setq first nil)
+      (insert (car lines))
+      (setq lines (cdr lines)))))
+
+;;;###autoload
+(defun open-rectangle (start end &optional fill)
+  "Blank out the region-rectangle, shifting text right.
+
+When called from a program, the rectangle's corners are START and END.
+With a prefix (or FILL) argument, fill with blanks even if there is no text
+on the right side of the rectangle."
+  (interactive "*r\nP")
+  (apply-on-rectangle 'open-rectangle-line start end fill)
+  (goto-char start))
+
+(defun open-rectangle-line (startcol endcol fill)
+  (let (spaces)
+    (when (= (move-to-column startcol (or fill 'coerce)) startcol)
+      (unless (and (not fill)
+		   (= (point) (point-at-eol)))
+	(indent-to endcol)))
+    ))
+
+;;;###autoload
+(defun string-rectangle (start end string)
+  "Insert STRING on each line of the region-rectangle, shifting text right.
+The left edge of the rectangle specifies the column for insertion. This
+command does not delete or overwrite any existing text.
+
+When called from a program, the rectangle's corners are START and END."
+  (interactive "*r\nsString rectangle: ")
+  (apply-on-rectangle 'string-rectangle-line start end string))
+
+(defun string-rectangle-line (startcol endcol string)
+  (move-to-column startcol t)
+  (insert string))
+
+;;;###autoload
+(defun clear-rectangle (start end &optional fill)
+  "Blank out the region-rectangle.
+The text previously in the region is overwritten with blanks.
+
+When called from a program, the rectangle's corners are START and END.
+With a prefix (or FILL) argument, also fill with blanks the parts of the
+rectangle which were empty."
+  (interactive "*r\nP")
+  (apply-on-rectangle 'clear-rectangle-line start end fill))
+
+(defun clear-rectangle-line (startcol endcol fill)
+  (let ((pt (point-at-eol))
+	spaces)
+    (when (= (move-to-column startcol (or fill 'coerce)) startcol)
+      (if (and (not fill)
+	       (<= (save-excursion (goto-char pt) (current-column)) endcol))
+	  (delete-region (point) pt)
+	;; else
+	(setq pt (point))
+	(move-to-column endcol t)
+	(setq spaces (- (point) pt))
+	(delete-region pt (point))
+	(indent-to (+ (current-column) spaces))))
+    ))
+
+(provide 'rect)
+
+;;; rect.el ends here