Initial Commit
[packages] / xemacs-packages / c-support / c-fill.el
1 ;;; C comment mode - An auto-filled comment mode for gnu c-mode.
2 ;;;
3 ;;; Author:     Robert Mecklenburg
4 ;;;             Computer Science Dept.
5 ;;;             University of Utah
6 ;;; From: mecklen@utah-gr.UUCP (Robert Mecklenburg)
7 ;;;   Also hartzell@Boulder.Colorado.EDU
8 ;;; (c) 1986, University of Utah
9 ;;;
10 ;;; Everyone is granted permission to copy, modify and redistribute
11 ;;; this file, provided the people they give it to can.
12
13 ;;; Synched up with: Not in FSF.
14
15 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
16 ;;;
17 ;;; I have written a "global comment" minor-mode which performs auto-fill,
18 ;;; fill-paragraph, and auto-indentation functions.  This function only
19 ;;; works for comments which occupy an entire line (not comments to the
20 ;;; right of code).  The mode has several options set through variables.
21 ;;; If the variable c-comment-starting-blank is non-nil multi-line
22 ;;; comments come out like this:
23 ;;; 
24 ;;;     /*
25 ;;;      * Your favorite 
26 ;;;      * multi-line comment.
27 ;;;      */
28 ;;; 
29 ;;; otherwise they look like this:
30 ;;; 
31 ;;;     /* Your Favorite
32 ;;;      * multi-line comment.
33 ;;;      */
34 ;;; 
35 ;;; If the variable c-comment-hanging-indent is non-nil K&R style comments
36 ;;; are indented automatically like this:
37 ;;; 
38 ;;;     /* my_func - For multi-line comments with hanging indent
39 ;;;      *           the text is lined up after the dash.
40 ;;;      */
41 ;;; 
42 ;;; otherwise the text "the text" (!) is lined up under my_func.  If a
43 ;;; comment fits (as typed) on a single line it remains a single line
44 ;;; comment even if c-comment-starting-blank is set.  If
45 ;;; c-comment-indenting is non-nil hitting carriage return resets the
46 ;;; indentation for the next line to the current line's indentation
47 ;;; (within the comment) like this:
48 ;;; 
49 ;;;     /* Typing along merrily....
50 ;;;      *     Now I indent with spaces, when I hit return
51 ;;;      *     the indentation is automatically set to 
52 ;;;      *     ^ here.
53 ;;;      */
54 ;;; 
55 ;;; Due to my lack of understanding of keymaps this permanently resets M-q
56 ;;; to my own fill function.  I would like to have the comment mode
57 ;;; bindings only in comment mode but I can't seem to get that to work.
58 ;;; If some gnu guru can clue me in, I'd appreciate it.
59 ;;;
60 (defvar c-comment-starting-blank t
61   "*Controls whether global comments have an initial blank line.")
62 (defvar c-comment-indenting t
63   "*If set global comments are indented to the level of the previous line.")
64 (defvar c-comment-hanging-indent t
65   "*If true, comments will be automatically indented to the dash.")
66 (defvar c-hang-already-done t
67   "If true we have performed the hanging indent already for this comment.")
68
69
70 ;;;
71 ;;; c-comment-map - This is a sparse keymap for comment mode which
72 ;;;                 gets inserted when c-comment is called.
73 ;;; 
74 (defvar c-comment-mode-map ()
75   "Keymap used in C comment mode.")
76 (if c-comment-mode-map
77     ()
78   (setq c-comment-mode-map (copy-keymap c-mode-map))
79   (define-key c-comment-mode-map "\e\r" 'newline)
80   (define-key c-comment-mode-map "\eq" 'set-fill-and-fill)
81   (define-key c-comment-mode-map "\r" 'set-fill-and-return))
82  
83 ;;;
84 ;;; c-comment - This is a filled comment mode which can format
85 ;;;             indented text, do hanging indents, and symetric
86 ;;;             placement of comment delimiters.
87 ;;; 
88 (defun c-comment ()
89   "Edit a C comment with filling and indentation.
90 This performs hanging indentation, symmetric placement of delimiters,
91  and Indented-Text mode style indentation.  Type 'M-x apropos
92 c-comment' for information on options."
93   (interactive)
94   (let
95       ;; Save old state.
96       ((auto-fill-function (if c-comment-indenting
97                                'do-indented-auto-fill 'do-auto-fill))
98 ;       (comment-start nil)
99        (comment-multi-line t)
100        (comment-start-skip "/*\\*+[     ]*")
101        (paragraph-start-ref paragraph-start)
102        fill-prefix paragraph-start paragraph-separate opoint)
103
104     ;; Determine if we are inside a comment.
105     (setq in-comment
106           (save-excursion
107             (and (re-search-backward "/\\*\\|\\*/" 0 t)
108                  (string= "/*" (buffer-substring (point) (+ (point) 2))))))
109
110     ;; Indent the comment and set the fill prefix to comment continuation
111     ;; string.  If we are already in a comment get the indentation on
112     ;; the current line.
113     (setq c-hang-already-done nil)
114
115     ;; Set the beginning of the comment and insert the blank line if needed.
116     (use-local-map c-comment-mode-map)
117     (if (not in-comment)
118         (progn (c-indent-line)
119                (insert "/* ")
120                (setq fill-prefix (get-current-fill (point)))
121                (recursive-edit)
122
123                ;; If the comment fits on one line, place the close
124                ;; comment at the end of the line.  Otherwise, newline.
125                (setq opoint (point))
126                (if (and (save-excursion (beginning-of-line)
127                                         (search-forward "/*" opoint t))
128                         (<= (+ (current-column) 3) 79))
129                    (insert " */")
130                  (insert "\n*/"))
131
132                (c-indent-line))
133       (progn (setq fill-prefix (get-current-fill (point)))
134              (recursive-edit)
135              (search-forward "*/" (buffer-size) t)
136              (forward-line 1)))
137
138     ;; If starting blank enabled, insert a newline, etc., but only if
139     ;; this comment requires multiple lines.
140     (if c-comment-starting-blank
141         (save-excursion
142           (setq opoint (point))
143           (forward-line -1)
144           (if (or (null (search-forward "/*" opoint t))
145                   (null (search-forward "*/" opoint t)))
146               (progn
147                 (search-backward "/*")
148                 (re-search-forward comment-start-skip opoint t)
149                 (setq fill-prefix (get-current-fill (point)))
150                 (if (not (looking-at "\n"))
151                     (insert ?\n fill-prefix))))))
152 ;                   (indent-new-comment-line))))))
153
154     ;; Move cursor to indentation.
155     (c-indent-line)
156     (use-local-map c-mode-map)
157     )
158   )
159
160
161 ;;;
162 ;;; set-fill-and-fill - Get the current fill for this line and fill
163 ;;;                     the paragraph.
164 ;;; 
165 (defun set-fill-and-fill (arg)
166   "Get the fill-prefix and fill the current paragraph."
167
168   (interactive "P")
169   (setq fill-prefix (get-current-fill (point)))
170   (fill-paragraph arg))
171
172 ;;;
173 ;;; set-fill-and-return - Set the current fill prefix and
174 ;;;                       indent-new-comment-line.
175 ;;; 
176 (defun set-fill-and-return ()
177   "Set the current fill prefix and move to the next line."
178
179   (interactive)
180   (if c-comment-indenting
181       (setq fill-prefix (get-current-fill (point))))
182   (insert ?\n fill-prefix))
183
184 ;;;
185 ;;; do-indented-auto-fill - Perform the auto-fill function, but get
186 ;;;                         the fill-prefix first.
187 ;;; 
188 (defun do-indented-auto-fill ()
189   "Perform auto-fill, but get fill-prefix first."
190
191   (let ((opoint (point)))
192     (save-excursion
193       (move-to-column (1+ fill-column))
194       (skip-chars-backward "^ \t\n")
195       (if (bolp)
196           (re-search-forward "[ \t]" opoint t))
197       ;; If there is a space on the line before fill-point,
198       ;; and nonspaces precede it, break the line there.
199       (if (save-excursion
200             (skip-chars-backward " \t")
201             (not (bolp)))
202
203           ;; If we are wrapping to a new line, figure out the indentation on
204           ;; the current line first.
205           (progn
206             (setq fill-prefix (get-current-fill opoint))
207             (insert ?\n fill-prefix)))))
208 ;           (indent-new-comment-line)))))
209   )
210
211
212 ;;;
213 ;;; get-current-fill - Get the fill-prefix for the current line.  This
214 ;;;                    assumes that the valid fill prefix is between
215 ;;;                    (beginning-of-line) and (point).
216 ;;; 
217 (defun get-current-fill (pnt)
218   "Get the current fill prefix.
219 A valid fill prefix must be between the beginning of the line and point."
220
221   (let ((opoint pnt) fill last-char)
222     (save-excursion
223       (beginning-of-line)
224       (setq fill
225             (buffer-substring (point)
226                               (progn
227                                 (re-search-forward comment-start-skip opoint t)
228                                 (point))))
229
230       ;; Be sure there is trailing white space.
231       (setq last-char (substring fill (1- (length fill)) (length fill)))
232       (if (and (not (string= " " last-char))
233                (not (string= "  " last-char)))
234           (setq fill (concat fill " ")))
235
236       (setq fill (replace-letter fill "/" " "))
237
238       ;; Get the hanging indentation if we haven't already.
239       (if (and c-comment-hanging-indent (not c-hang-already-done))
240           (let ((curr (point))
241                 (opnt (progn (end-of-line) (point))))
242             (beginning-of-line)
243             (if (search-forward " - " opnt t)
244                 (progn
245                   (setq fill (concat fill (make-string (- (point) curr) 32)))
246                   (setq c-hang-already-done t)))))
247
248       ;; Set the paragraph delimiters.
249       (setq paragraph-start (concat paragraph-start-ref
250                                     "\\|^"
251                                     (regexp-quote
252                                      (substring fill
253                                                 0 (1- (length fill))))
254                                     "$"))
255       (setq paragraph-separate paragraph-start))
256     fill)
257   )
258   
259
260 ;;;
261 ;;; replace-letter - Given a string, an old letter and a new letter,
262 ;;;                  perform the substitution.
263 ;;; 
264 (defun replace-letter (str old-letter new-letter)
265   (let (new-str c
266         (sp 0)
267         (size (length str)))
268     (while (< sp size)
269       (setq c (substring str sp (1+ sp)))
270       (setq new-str (concat new-str (if (string= c old-letter) new-letter c)))
271       (setq sp (1+ sp)))
272     new-str))