[comp.emacs] Looking for an insert at column function

frankh@durin.sparta.COM (Frank Halsema) (06/16/89)

Does emacs have or does someone else have a function that will
isert a string in a region at a specified column. The function
would be similiar to one that exists in a editor that I have used.
The syntax of the old editor was

              ic 20-40 c5 hello

This command would insert the string hello starting in column 
5 between line 20 and 40.

In emacs one would use a region instead of a line range. The lack of
(or ignorance as the case may be) such a function has been the only
complaint about emacs (at least the only one worth giving credence).

Thanks for any help.
                                                               *
                                                             * *
Frank Halsema                                             /\* *
SPARTA, Inc.                                             / .\*
							/ /\.\
						       / .\.  \
						       \...\  /
						       *\.\/ /
                                                      * *\  /
durin!frankh                                         ***  \/

quiroz@cs.rochester.edu (06/22/89)

This bounced in the mail, so I'd better post it.

Enjoy,
------- Forwarded Message

Received: from cayuga.cs.rochester.edu by sol.cs.rochester.edu (3.2/m) id AA19717; Wed, 21 Jun 89 18:05:59 EDT
Received: by cayuga.cs.rochester.edu (5.59/m) id AA25064; Wed, 21 Jun 89 18:03:24 EDT
Date: Wed, 21 Jun 89 18:03:24 EDT
From: Mail Delivery Subsystem  <MAILER-DAEMON>
Subject: Returned mail: Cannot send message for 3 days
Message-Id: <8906212203.AA25064@cayuga.cs.rochester.edu>
To: <quiroz>

   ----- Transcript of session follows -----

   ----- Unsent message follows -----
Received: from lesath.cs.rochester.edu by cayuga.cs.rochester.edu (5.59/m) id AA11374; Sun, 18 Jun 89 18:04:48 EDT
Received: from loopback by lesath.cs.rochester.edu (3.2/m) id AA00924; Sun, 18 Jun 89 18:05:12 EDT
Message-Id: <8906182205.AA00924@lesath.cs.rochester.edu>
To: frankh@durin.sparta.COM (Frank Halsema)
Subject: Re: Looking for an insert at column function 
In-Reply-To: Your message of 16 Jun 89 07:28:57 +0000.
             <1220@durin.sparta.COM> 
Date: Sun, 18 Jun 89 18:05:07 -0400
From: quiroz

Out of curiosity, I consed up a function the responds directly your
interactive needs.  Hope you find it useful.

Cesar
- ---The Legendary Dashed Line---------------------------------------
(defun ic (start end column string)
  "For lines in the range of numbers START and END (START <= END),
insert at column COLUMN the string STRING.  Extend the file, if
needed, to make the ranges implied possible."
  (interactive "nStarting line: \nnEnding line: \nnColumn: \nsString: ")
  (let* ((lastline (count-lines (point-min) (point-max)))
         (deficit  (- end lastline)))
    (if (< end start)
        (error "End (%d) should be greater or equal to start (%d)"
               end start))
    (goto-char (point-max))
    (while (plusp deficit)
      (insert "\n")
      (setq deficit (- deficit 1)))
    (let ((current-line start))
      (while (<= current-line end)
        (goto-line current-line)
        (goto-column column)
        (insert string)
        (setq current-line (+ current-line 1))))))

(defun goto-column (n)
  "Go to column N in this line, extend line at end with spaces if
needed.  Interactively, take the prefix argument as a column number or
query in the minibuffer if none given."
  (interactive "p")
  (if (and (interactive-p) (null current-prefix-arg))
      (setq n (call-interactively
               '(lambda (n) (interactive "nColumn to go to? ") n))))
  (if (< n 0)
      (error "Negative column `%d' given to goto-column." n))
  (let* ((line-length (save-excursion (end-of-line)
                                      (current-column)))
        (difference   (- n line-length)))
    (cond ((<= difference 0)
           (move-to-column n))
          (t
           (end-of-line)
           (while (> difference 0)
             (insert " ")
             (setq difference (- difference 1)))))))

------- End of Forwarded Message

quiroz@cs.rochester.edu (07/22/89)

At the end of this posting is a program (generic-code-mode) I use
whenever dealing with roughly block strutured files (like shell
scripts, awk programs, etc.), for which there is no specific mode
that knows their syntax.  The General Public License applies, as
usual.  I haven't done much work on it for a while, but I use it
frequently.  I don't expect bugs, but I am not happy with the fake
unix-script-mode.  So I am posting it in `pre-release' condition,
for criticism and improvement from the user community.

Frank: The function you need can be synthesized from `goto-column'.
(I think you can separate goto-column from the rest, you may not
need the bulk of generic-code-mode for this application).  Suppose
you need to insert a string at column N (remember it is 0-based) _in
the current line_, you would just go there and insert.  So, if you
need to navigate by lines and columns, I would use something like
this (vastly untested, careful):

;;; Bug: if line LINE does not exist, you get to the end of the
;;; buffer.  Exercise for the reader: using count-lines or its
;;; friends, first figure out if the buffer needs to be stretched.
;;; Another exercise:  figure out a reasonable interactive setting.
(defun go-and-insert (string column &optional line)
  "Drop STRING at COLUMN of LINE.  Omitting LINE defaults to the current line,
the line point is on.  LINE counts from 1 at the top of the buffer, COLUMN
counts from 0 at the left edge.  Point is left at the end of the inserted
string."
  ;; you wouldn't want this to be (interactive), now would you?
  (if line (goto-line line))
  (goto-column column)
  (insert string))

The peculiar argument order is justified by the desire to make the
line number optional, of course.

Cesar
;; Editing generic block structured files
;; Pre-release. 
;; Copyright (C) 1989 Free Software Foundation, Inc.

;; Although this file is not yet part of GNU Emacs, it is
;; distributed in the same spirit.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.

;;; Generic code mode for EMACS.
;;;
;;; Integrated more stuff.  Point-to-tab-stop lets you move point
;;; without adding whitespace.  Goto-column lets you go to a column of
;;; your choice, even when the line is shorter than that.  CQ, 14 sep 88.
;;;
;;; This library lets the user deal with editing inputs that look
;;; block structured and have optional comments.  It cannot be too
;;; bright, as there is little or no indication of the intended
;;; syntax, but the user can indicate his preferences via syntactic
;;; classes, abbrevs and such.  CQ, 30 sep 87.

(provide 'generic-code-mode)
(require 'cl)

(defvar generic-code-mode-hook nil)
(defvar generic-code-mode-syntax-table nil)
(defvar generic-code-mode-abbrev-table nil)
(defvar generic-code-mode-map nil)

(define-abbrev-table 'generic-code-mode-abbrev-table ())

(cond ((null generic-code-mode-map)
       (setq generic-code-mode-map (copy-keymap indented-text-mode-map))
       (define-key generic-code-mode-map "\M-o"    'indent-to-point)
       (define-key generic-code-mode-map "\C-i"    'indent-to-tab-stop)
       (define-key generic-code-mode-map "\M-\C-i" 'tab-to-tab-stop)
       (define-key generic-code-mode-map "\M-i"    'point-to-tab-stop)
       (define-key generic-code-mode-map "\C-x|"   'goto-column)
       ))

(defun generic-code-mode ()
  "Sets generic code mode, essentially indented-text mode
with no fill.  TAB is rebound to run indent-to-tab-stop (q.v.),
which is used to advance or retreat the indentation of the 
current line.  See the function `set-scripts-syntax' for a simple
customization useful when editing shell scripts, awk programs or icon
programs, and the function `unix-script-mode' for a simple way to use
it.  See `right-adjust-line' too. 
\\{generic-code-mode-map}"
  (interactive)
  (indented-text-mode)
  ;; the variables specific to generic code mode are made buffer-local
  ;; in order to preserve the possibility of editing simultaneously
  ;; with slightly different `less-generic' modes based on this one.
  (make-local-variable 'generic-code-mode-map)
  (use-local-map generic-code-mode-map)
  (auto-fill-mode -1)
  (setq major-mode 'generic-code-mode)
  (setq mode-name "Code")
  (make-local-variable 'generic-code-mode-syntax-table)
  (setq generic-code-mode-syntax-table (make-syntax-table))
  (make-local-variable 'generic-code-mode-syntax-table)
  (set-syntax-table generic-code-mode-syntax-table)
  (make-local-variable 'generic-code-mode-abbrev-table)
  (setq local-abbrev-table generic-code-mode-abbrev-table)
  ;;
  (make-local-variable 'comment-start)
  (make-local-variable 'comment-end)
  (make-local-variable 'comment-start-skip)
  (make-local-variable 'comment-indent-hook)
  ;;
  (run-hooks 'generic-code-mode-hook))

(defun indent-to-tab-stop (n)
  "Advance or retreat the indentation of this line.
A positive argument (interactive default) advances,
a negative argument retreats,
a zero argument aligns with the previous nonblank line.
The number of tab stops actually moved is the absolute value of the
argument.
Just using \C-u as prefix means the same as -1."
  (interactive "p")
  ;; Special case suggested by Neil:  Just giving
  ;; `C-u' means `-', instead of `M- 4'.
  (when (consp current-prefix-arg)      ;Any number of `C-u's
    (setq n -1))
  (let ((here (point-marker))
        (do-not-return nil))
    (back-to-indentation)
    (setq do-not-return (looking-at "[ \t]*$"))
    (multiple-value-bind
        (prev-tab next-tab)
        (find-neighboring-tabs (current-column))
      ;; Depending on sign of n, move forward or backward
      (cond ((> n 0)
             (indent-to-column next-tab)
             (when (> (decf n) 0)
               (indent-to-tab-stop n)))
            ((= n 0)
             (delete-horizontal-space)
             (indent-relative-maybe))
            ((< n 0)
             (delete-horizontal-space)
             (indent-to-column prev-tab)
             (when (< (incf n) 0)
               (indent-to-tab-stop n)))))
    ;; go back to the correct relative position
    (unless do-not-return
      (goto-char (marker-position here)))))

(defun point-to-tab-stop (n)
  "Advance or retreat the current-column to a near tab stop.  Just
move the cursor, don't add nor delete anything.  Compare with
tab-to-tab-stop, that pushes things to the right.
A positive argument (interactive default) advances, 
a negative argument retreats.
The number of tab stops actually moved is the absolute value of the
argument.  Giving it \C-u means `-', not 4."
  (interactive "p")
  ;; Special case suggested by Neil:  Just giving
  ;; `C-u' means `-', instead of `M- 4'.
  (when (consp current-prefix-arg)      ;Any number of `C-u's
    (setq n -1))
  (multiple-value-bind
      (prev-tab next-tab)
      (find-neighboring-tabs (current-column))
    ;; Depending on sign of n, move forward or backward
    (cond ((> n 0)
           (goto-column next-tab)
           (when (> (decf n) 0)
             (point-to-tab-stop n)))
          ((< n 0)
           (goto-column prev-tab)
           (when (< (incf n) 0)
             (point-to-tab-stop n))))))

(defun find-neighboring-tabs (column)
  "Return previous and next tab positions, when at position COLUMN.
The values returned are chosen from tab-stop-list (if not null) or
computed as exact multiples of tab-width.  If COLUMN is outside the
range of tab-stop-list, either 0 or (+ Column Tab-Width) are used as
the missing bounds."
  (cond  ((null tab-stop-list)          ; have to use multiples of tab-width
          (cond ((= (mod column tab-width) 0) ;exact match
                 (values (max 0 (- column tab-width)) (+ column tab-width)))
                (t                      ;in between matches
                 (let* ((prev (floor column tab-width))
                        (next (+ prev tab-width)))
                   (values prev next)))))
         (t                             ;there is a tab-stop-list
          (do* ((tabs                   ;add special terminator
                 (append tab-stop-list (list (+ column tab-width)))
                 (cdr tabs))
                (past 0 this)
                (this (car tabs) (car tabs))
                (next)                  ;used to store the values
                (prev)                  ; to return.
                (done nil))
              (done (values prev next))
            ;; we compare the current column against each tab stop
            ;; (the tab-stop-list being augmented by a catch-all
            ;; terminator) until the current column either matches one
            ;; of the tab stops or is precisely contained between two
            ;; of them.
            (cond ((= column this)
                   ;; column matches a tab-stop=>return surrounding ones.
                   (setf prev past      ;need to protect value of past!
                         next (cadr tabs) ;cadr won't ever fail here
                         done t))
                  ((and (>= column past)
                        (<  column this))
                   ;; column is in between past and this
                   (setf prev past      ;need to protect value of past!
                         next this
                         done t))
                  (t
                   ;; no need to do anything, do* will step for us
                   ))))))

(defun indent-to-point (here)
  "Move indentation of this line to the column of point.
Used to force indentation to a given spot.  When called from a
program, give it a character position.  The line that contains that
position will be indented to the column of that position in it."
  (interactive "d")
  ;; Make sure you are really HERE (in the line containing HERE)
  (goto-char here)
  (let ((indentation (current-column)))
    (beginning-of-line)
    (delete-horizontal-space)
    (indent-to-column indentation)))

;;; Customizations of code mode

(defun unix-script-mode ()
  "This function sets up generic-code-mode with the syntax for scripts
that is provided by set-scripts-syntax.  It is not a real major mode,
so it appears to be generic-code-mode for all intents and purposes."
  (interactive)
  (generic-code-mode)
  (set-scripts-syntax))

(defun set-scripts-syntax ()
  "Establish # as a comment character, etc...  Modifies
generic-code-mode such that it is useful for scripts to
be fed to sh, csh, awk, icont and the like."
  (interactive)
  ;; special commenting conventions
  (modify-syntax-entry ?# "<")
  (modify-syntax-entry ?\n ">")
  (modify-syntax-entry ?\f ">")
  (setq comment-start "#")
  (setq comment-end "")
  (setq comment-start-skip "#+ *")
  (setq comment-indent-hook 'generic-code-hash-comment-indent))

(defun generic-code-hash-comment-indent ()
  "Indent comments that begin with at least one #.
Comments at the beginning of a line or that begin with more than one
hash are left alone.  Move them around with indent-to-tab-stop, if you
must."
  (if (or (bolp) (looking-at "##"))
      (current-column)
    (skip-chars-backward " \t")
    (max (if (bolp) 0 (1+ (current-column)))
         comment-column)))

;;; This routine provides absolute motions in a line, with possible
;;; extension of the line if so requested.  Note that the standard
;;; `move-to-column' stops at line end.  I bind this usually to `C-x |'.

(defun goto-column (n)
  "Go to column N in this line, extend line at end with spaces if
needed.  Interactively, take the prefix argument as a column number or
query in the minibuffer if none given."
  (interactive "p")
  (if (and (interactive-p) (null current-prefix-arg))
      (setq n (call-interactively
               '(lambda (n) (interactive "nColumn to go to? ") n))))
  (if (< n 0)
      (error "Negative column `%d' given to goto-column." n))
  (let* ((line-length (save-excursion (end-of-line)
                                      (current-column)))
        (difference   (- n line-length)))
    (cond ((<= difference 0)
           (move-to-column n))
          (t
           (end-of-line)
           (while (> difference 0)
             (insert " ")
             (setq difference (- difference 1)))))))

(defun right-adjust-line ()
  "Have the current line end in a non-whitespace character aligned to
the rightmost position.  Rightmost here means 'at the fill-column or
at window end'.  Sort of complements any usage of `center-line' and
the usage of `delete-horizontal-space' at the beginning of a line."
  (interactive)
  (beginning-of-line)
  (let ((had-fill-prefix    (and fill-prefix (looking-at fill-prefix)))
        (fill-prefix-length (length fill-prefix)))        
    (if had-fill-prefix
        (delete-char fill-prefix-length))
    ;; trim horizontal space around the remaining text
    (delete-horizontal-space)
    (end-of-line)
    (delete-horizontal-space)
    ;; ASSERTION: Cursor is now at end of line
    (let* ((text-length  (current-column))
           (line-length  (if fill-column
                             fill-column
                           (window-width)))
           (slack        (- line-length text-length)))
      (beginning-of-line)
      (if (> slack 0)
          (indent-to-column slack))
      ;; restore fill-prefix, if removed earlier
      (cond  (had-fill-prefix
              (beginning-of-line)
              (cond ((> slack fill-prefix-length)
                     (delete-char fill-prefix-length)
                     (insert fill-prefix))
                    (t                  ;give up on this
                     (delete-char slack)
                     (insert fill-prefix)))))
      ;; have to put the cursor somewhere...
      (end-of-line))))

;;; end of generic-code-mode.el

quiroz@cs.rochester.edu (Cesar Quiroz) (07/22/89)

At the end of this posting is a program (generic-code-mode) I use
whenever dealing with roughly block structured files (like shell
scripts, awk programs, etc.), for which there is no specific mode
that knows their syntax.  The General Public License applies, as
usual.  I haven't done much work on it for a while, but I use it
frequently.  I don't expect bugs, but I am not happy with the fake
unix-script-mode.  So I am posting it in `pre-release' condition,
for criticism and improvement from the user community.

Frank: The function you need can be synthesized from `goto-column'.
(I think you can separate goto-column from the rest, you may not
need the bulk of generic-code-mode for this application).  Suppose
you need to insert a string at column N (remember it is 0-based) _in
the current line_, you would just go there and insert.  So, if you
need to navigate by lines and columns, I would use something like
this (vastly untested, careful):

;;; Bug: if line LINE does not exist, you get to the end of the
;;; buffer.  Exercise for the reader: using count-lines or its
;;; friends, first figure out if the buffer needs to be stretched.
;;; Another exercise:  figure out a reasonable interactive setting.
(defun go-and-insert (string column &optional line)
  "Drop STRING at COLUMN of LINE.  Omitting LINE defaults to the current line,
the line point is on.  LINE counts from 1 at the top of the buffer, COLUMN
counts from 0 at the left edge.  Point is left at the end of the inserted
string."
  ;; you wouldn't want this to be (interactive), now would you?
  (if line (goto-line line))
  (goto-column column)
  (insert string))

The peculiar argument order is justified by the desire to make the
line number optional, of course.  The function you need requires
more work (has to drop the string more than once, etc.), but it
shouldn't be hard to modify go-and-insert to accomplish it.

Cesar
;; Editing generic block structured files
;; Pre-release. 
;; Copyright (C) 1989 Free Software Foundation, Inc.

;; Although this file is not yet part of GNU Emacs, it is
;; distributed in the same spirit.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.

;;; Generic code mode for EMACS.
;;;
;;; Integrated more stuff.  Point-to-tab-stop lets you move point
;;; without adding whitespace.  Goto-column lets you go to a column of
;;; your choice, even when the line is shorter than that.  CQ, 14 sep 88.
;;;
;;; This library lets the user deal with editing inputs that look
;;; block structured and have optional comments.  It cannot be too
;;; bright, as there is little or no indication of the intended
;;; syntax, but the user can indicate his preferences via syntactic
;;; classes, abbrevs and such.  CQ, 30 sep 87.

(provide 'generic-code-mode)
(require 'cl)

(defvar generic-code-mode-hook nil)
(defvar generic-code-mode-syntax-table nil)
(defvar generic-code-mode-abbrev-table nil)
(defvar generic-code-mode-map nil)

(define-abbrev-table 'generic-code-mode-abbrev-table ())

(cond ((null generic-code-mode-map)
       (setq generic-code-mode-map (copy-keymap indented-text-mode-map))
       (define-key generic-code-mode-map "\M-o"    'indent-to-point)
       (define-key generic-code-mode-map "\C-i"    'indent-to-tab-stop)
       (define-key generic-code-mode-map "\M-\C-i" 'tab-to-tab-stop)
       (define-key generic-code-mode-map "\M-i"    'point-to-tab-stop)
       (define-key generic-code-mode-map "\C-x|"   'goto-column)
       ))

(defun generic-code-mode ()
  "Sets generic code mode, essentially indented-text mode
with no fill.  TAB is rebound to run indent-to-tab-stop (q.v.),
which is used to advance or retreat the indentation of the 
current line.  See the function `set-scripts-syntax' for a simple
customization useful when editing shell scripts, awk programs or icon
programs, and the function `unix-script-mode' for a simple way to use
it.  See `right-adjust-line' too. 
\\{generic-code-mode-map}"
  (interactive)
  (indented-text-mode)
  ;; the variables specific to generic code mode are made buffer-local
  ;; in order to preserve the possibility of editing simultaneously
  ;; with slightly different `less-generic' modes based on this one.
  (make-local-variable 'generic-code-mode-map)
  (use-local-map generic-code-mode-map)
  (auto-fill-mode -1)
  (setq major-mode 'generic-code-mode)
  (setq mode-name "Code")
  (make-local-variable 'generic-code-mode-syntax-table)
  (setq generic-code-mode-syntax-table (make-syntax-table))
  (make-local-variable 'generic-code-mode-syntax-table)
  (set-syntax-table generic-code-mode-syntax-table)
  (make-local-variable 'generic-code-mode-abbrev-table)
  (setq local-abbrev-table generic-code-mode-abbrev-table)
  ;;
  (make-local-variable 'comment-start)
  (make-local-variable 'comment-end)
  (make-local-variable 'comment-start-skip)
  (make-local-variable 'comment-indent-hook)
  ;;
  (run-hooks 'generic-code-mode-hook))

(defun indent-to-tab-stop (n)
  "Advance or retreat the indentation of this line.
A positive argument (interactive default) advances,
a negative argument retreats,
a zero argument aligns with the previous nonblank line.
The number of tab stops actually moved is the absolute value of the
argument.
Just using \C-u as prefix means the same as -1."
  (interactive "p")
  ;; Special case suggested by Neil:  Just giving
  ;; `C-u' means `-', instead of `M- 4'.
  (when (consp current-prefix-arg)      ;Any number of `C-u's
    (setq n -1))
  (let ((here (point-marker))
        (do-not-return nil))
    (back-to-indentation)
    (setq do-not-return (looking-at "[ \t]*$"))
    (multiple-value-bind
        (prev-tab next-tab)
        (find-neighboring-tabs (current-column))
      ;; Depending on sign of n, move forward or backward
      (cond ((> n 0)
             (indent-to-column next-tab)
             (when (> (decf n) 0)
               (indent-to-tab-stop n)))
            ((= n 0)
             (delete-horizontal-space)
             (indent-relative-maybe))
            ((< n 0)
             (delete-horizontal-space)
             (indent-to-column prev-tab)
             (when (< (incf n) 0)
               (indent-to-tab-stop n)))))
    ;; go back to the correct relative position
    (unless do-not-return
      (goto-char (marker-position here)))))

(defun point-to-tab-stop (n)
  "Advance or retreat the current-column to a near tab stop.  Just
move the cursor, don't add nor delete anything.  Compare with
tab-to-tab-stop, that pushes things to the right.
A positive argument (interactive default) advances, 
a negative argument retreats.
The number of tab stops actually moved is the absolute value of the
argument.  Giving it \C-u means `-', not 4."
  (interactive "p")
  ;; Special case suggested by Neil:  Just giving
  ;; `C-u' means `-', instead of `M- 4'.
  (when (consp current-prefix-arg)      ;Any number of `C-u's
    (setq n -1))
  (multiple-value-bind
      (prev-tab next-tab)
      (find-neighboring-tabs (current-column))
    ;; Depending on sign of n, move forward or backward
    (cond ((> n 0)
           (goto-column next-tab)
           (when (> (decf n) 0)
             (point-to-tab-stop n)))
          ((< n 0)
           (goto-column prev-tab)
           (when (< (incf n) 0)
             (point-to-tab-stop n))))))

(defun find-neighboring-tabs (column)
  "Return previous and next tab positions, when at position COLUMN.
The values returned are chosen from tab-stop-list (if not null) or
computed as exact multiples of tab-width.  If COLUMN is outside the
range of tab-stop-list, either 0 or (+ Column Tab-Width) are used as
the missing bounds."
  (cond  ((null tab-stop-list)          ; have to use multiples of tab-width
          (cond ((= (mod column tab-width) 0) ;exact match
                 (values (max 0 (- column tab-width)) (+ column tab-width)))
                (t                      ;in between matches
                 (let* ((prev (floor column tab-width))
                        (next (+ prev tab-width)))
                   (values prev next)))))
         (t                             ;there is a tab-stop-list
          (do* ((tabs                   ;add special terminator
                 (append tab-stop-list (list (+ column tab-width)))
                 (cdr tabs))
                (past 0 this)
                (this (car tabs) (car tabs))
                (next)                  ;used to store the values
                (prev)                  ; to return.
                (done nil))
              (done (values prev next))
            ;; we compare the current column against each tab stop
            ;; (the tab-stop-list being augmented by a catch-all
            ;; terminator) until the current column either matches one
            ;; of the tab stops or is precisely contained between two
            ;; of them.
            (cond ((= column this)
                   ;; column matches a tab-stop=>return surrounding ones.
                   (setf prev past      ;need to protect value of past!
                         next (cadr tabs) ;cadr won't ever fail here
                         done t))
                  ((and (>= column past)
                        (<  column this))
                   ;; column is in between past and this
                   (setf prev past      ;need to protect value of past!
                         next this
                         done t))
                  (t
                   ;; no need to do anything, do* will step for us
                   ))))))

(defun indent-to-point (here)
  "Move indentation of this line to the column of point.
Used to force indentation to a given spot.  When called from a
program, give it a character position.  The line that contains that
position will be indented to the column of that position in it."
  (interactive "d")
  ;; Make sure you are really HERE (in the line containing HERE)
  (goto-char here)
  (let ((indentation (current-column)))
    (beginning-of-line)
    (delete-horizontal-space)
    (indent-to-column indentation)))

;;; Customizations of code mode

(defun unix-script-mode ()
  "This function sets up generic-code-mode with the syntax for scripts
that is provided by set-scripts-syntax.  It is not a real major mode,
so it appears to be generic-code-mode for all intents and purposes."
  (interactive)
  (generic-code-mode)
  (set-scripts-syntax))

(defun set-scripts-syntax ()
  "Establish # as a comment character, etc...  Modifies
generic-code-mode such that it is useful for scripts to
be fed to sh, csh, awk, icont and the like."
  (interactive)
  ;; special commenting conventions
  (modify-syntax-entry ?# "<")
  (modify-syntax-entry ?\n ">")
  (modify-syntax-entry ?\f ">")
  (setq comment-start "#")
  (setq comment-end "")
  (setq comment-start-skip "#+ *")
  (setq comment-indent-hook 'generic-code-hash-comment-indent))

(defun generic-code-hash-comment-indent ()
  "Indent comments that begin with at least one #.
Comments at the beginning of a line or that begin with more than one
hash are left alone.  Move them around with indent-to-tab-stop, if you
must."
  (if (or (bolp) (looking-at "##"))
      (current-column)
    (skip-chars-backward " \t")
    (max (if (bolp) 0 (1+ (current-column)))
         comment-column)))

;;; This routine provides absolute motions in a line, with possible
;;; extension of the line if so requested.  Note that the standard
;;; `move-to-column' stops at line end.  I bind this usually to `C-x |'.

(defun goto-column (n)
  "Go to column N in this line, extend line at end with spaces if
needed.  Interactively, take the prefix argument as a column number or
query in the minibuffer if none given."
  (interactive "p")
  (if (and (interactive-p) (null current-prefix-arg))
      (setq n (call-interactively
               '(lambda (n) (interactive "nColumn to go to? ") n))))
  (if (< n 0)
      (error "Negative column `%d' given to goto-column." n))
  (let* ((line-length (save-excursion (end-of-line)
                                      (current-column)))
        (difference   (- n line-length)))
    (cond ((<= difference 0)
           (move-to-column n))
          (t
           (end-of-line)
           (while (> difference 0)
             (insert " ")
             (setq difference (- difference 1)))))))

(defun right-adjust-line ()
  "Have the current line end in a non-whitespace character aligned to
the rightmost position.  Rightmost here means 'at the fill-column or
at window end'.  Sort of complements any usage of `center-line' and
the usage of `delete-horizontal-space' at the beginning of a line."
  (interactive)
  (beginning-of-line)
  (let ((had-fill-prefix    (and fill-prefix (looking-at fill-prefix)))
        (fill-prefix-length (length fill-prefix)))        
    (if had-fill-prefix
        (delete-char fill-prefix-length))
    ;; trim horizontal space around the remaining text
    (delete-horizontal-space)
    (end-of-line)
    (delete-horizontal-space)
    ;; ASSERTION: Cursor is now at end of line
    (let* ((text-length  (current-column))
           (line-length  (if fill-column
                             fill-column
                           (window-width)))
           (slack        (- line-length text-length)))
      (beginning-of-line)
      (if (> slack 0)
          (indent-to-column slack))
      ;; restore fill-prefix, if removed earlier
      (cond  (had-fill-prefix
              (beginning-of-line)
              (cond ((> slack fill-prefix-length)
                     (delete-char fill-prefix-length)
                     (insert fill-prefix))
                    (t                  ;give up on this
                     (delete-char slack)
                     (insert fill-prefix)))))
      ;; have to put the cursor somewhere...
      (end-of-line))))

;;; end of generic-code-mode.el

-- 
                                      Cesar Augusto Quiroz Gonzalez
                                      Department of Computer Science
                                      University of Rochester
                                      Rochester,  NY 14627