| 1 | ;;; Boxed comments for C mode. | 
|---|
| 2 | ;;; Copyright (C) 1991, 1992, 1993, 1994 Free Software Foundation, Inc. | 
|---|
| 3 | ;;; Francois Pinard <pinard@iro.umontreal.ca>, April 1991. | 
|---|
| 4 | ;;; | 
|---|
| 5 | ;;; I often refill paragraphs inside C comments, while stretching or | 
|---|
| 6 | ;;; shrinking the surrounding box as needed.  This is a real pain to | 
|---|
| 7 | ;;; do by hand.  Here is the code I made to ease my life on this, | 
|---|
| 8 | ;;; usable from within GNU Emacs.  It would not be fair giving all | 
|---|
| 9 | ;;; sources for a product without also giving the means for nicely | 
|---|
| 10 | ;;; modifying them. | 
|---|
| 11 | ;;; | 
|---|
| 12 | ;;; The function rebox-c-comment adjust comment boxes without | 
|---|
| 13 | ;;; refilling comment paragraphs, while reindent-c-comment adjust | 
|---|
| 14 | ;;; comment boxes after refilling.  Numeric prefixes are used to add, | 
|---|
| 15 | ;;; remove, or change the style of the box surrounding the comment. | 
|---|
| 16 | ;;; Since refilling paragraphs in C mode does make sense only for | 
|---|
| 17 | ;;; comments, this code redefines the M-q command in C mode.  I use | 
|---|
| 18 | ;;; this hack by putting, in my .emacs file: | 
|---|
| 19 | ;;; | 
|---|
| 20 | ;;;     (setq c-mode-hook | 
|---|
| 21 | ;;;           '(lambda () | 
|---|
| 22 | ;;;              (define-key c-mode-map "\M-q" 'reindent-c-comment))) | 
|---|
| 23 | ;;;     (autoload 'rebox-c-comment "c-boxes" nil t) | 
|---|
| 24 | ;;;     (autoload 'reindent-c-comment "c-boxes" nil t) | 
|---|
| 25 | ;;; | 
|---|
| 26 | ;;; The cursor should be within a comment before any of these | 
|---|
| 27 | ;;; commands, or else it should be between two comments, in which case | 
|---|
| 28 | ;;; the command applies to the next comment.  When the command is | 
|---|
| 29 | ;;; given without prefix, the current comment box type is recognized | 
|---|
| 30 | ;;; and preserved.  Given 0 as a prefix, the comment box disappears | 
|---|
| 31 | ;;; and the comment stays between a single opening `/*' and a single | 
|---|
| 32 | ;;; closing `*/'.  Given 1 or 2 as a prefix, a single or doubled lined | 
|---|
| 33 | ;;; comment box is forced.  Given 3 as a prefix, a Taarna style box is | 
|---|
| 34 | ;;; forced, but you do not even want to hear about those.  When a | 
|---|
| 35 | ;;; negative prefix is given, the absolute value is used, but the | 
|---|
| 36 | ;;; default style is changed.  Any other value (like C-u alone) forces | 
|---|
| 37 | ;;; the default box style. | 
|---|
| 38 | ;;; | 
|---|
| 39 | ;;; I observed rounded corners first in some code from Warren Tucker | 
|---|
| 40 | ;;; <wht@n4hgf.mt-park.ga.us>. | 
|---|
| 41 |  | 
|---|
| 42 | (defvar c-box-default-style 'single "*Preferred style for box comments.") | 
|---|
| 43 | (defvar c-mode-taarna-style nil "*Non-nil for Taarna team C-style.") | 
|---|
| 44 |  | 
|---|
| 45 | ;;; Set or reset the Taarna team's own way for a C style. | 
|---|
| 46 |  | 
|---|
| 47 | (defun taarna-mode () | 
|---|
| 48 | (interactive) | 
|---|
| 49 | (if c-mode-taarna-style | 
|---|
| 50 | (progn | 
|---|
| 51 |  | 
|---|
| 52 | (setq c-mode-taarna-style nil) | 
|---|
| 53 | (setq c-indent-level 2) | 
|---|
| 54 | (setq c-continued-statement-offset 2) | 
|---|
| 55 | (setq c-brace-offset 0) | 
|---|
| 56 | (setq c-argdecl-indent 5) | 
|---|
| 57 | (setq c-label-offset -2) | 
|---|
| 58 | (setq c-tab-always-indent t) | 
|---|
| 59 | (setq c-box-default-style 'single) | 
|---|
| 60 | (message "C mode: GNU style")) | 
|---|
| 61 |  | 
|---|
| 62 | (setq c-mode-taarna-style t) | 
|---|
| 63 | (setq c-indent-level 4) | 
|---|
| 64 | (setq c-continued-statement-offset 4) | 
|---|
| 65 | (setq c-brace-offset -4) | 
|---|
| 66 | (setq c-argdecl-indent 4) | 
|---|
| 67 | (setq c-label-offset -4) | 
|---|
| 68 | (setq c-tab-always-indent t) | 
|---|
| 69 | (setq c-box-default-style 'taarna) | 
|---|
| 70 | (message "C mode: Taarna style"))) | 
|---|
| 71 |  | 
|---|
| 72 | ;;; Return the minimum value of the left margin of all lines, or -1 if | 
|---|
| 73 | ;;; all lines are empty. | 
|---|
| 74 |  | 
|---|
| 75 | (defun buffer-left-margin () | 
|---|
| 76 | (let ((margin -1)) | 
|---|
| 77 | (goto-char (point-min)) | 
|---|
| 78 | (while (not (eobp)) | 
|---|
| 79 | (skip-chars-forward " \t") | 
|---|
| 80 | (if (not (looking-at "\n")) | 
|---|
| 81 | (setq margin | 
|---|
| 82 | (if (< margin 0) | 
|---|
| 83 | (current-column) | 
|---|
| 84 | (min margin (current-column))))) | 
|---|
| 85 | (forward-line 1)) | 
|---|
| 86 | margin)) | 
|---|
| 87 |  | 
|---|
| 88 | ;;; Return the maximum value of the right margin of all lines.  Any | 
|---|
| 89 | ;;; sentence ending a line has a space guaranteed before the margin. | 
|---|
| 90 |  | 
|---|
| 91 | (defun buffer-right-margin () | 
|---|
| 92 | (let ((margin 0) period) | 
|---|
| 93 | (goto-char (point-min)) | 
|---|
| 94 | (while (not (eobp)) | 
|---|
| 95 | (end-of-line) | 
|---|
| 96 | (if (bobp) | 
|---|
| 97 | (setq period 0) | 
|---|
| 98 | (backward-char 1) | 
|---|
| 99 | (setq period (if (looking-at "[.?!]") 1 0)) | 
|---|
| 100 | (forward-char 1)) | 
|---|
| 101 | (setq margin (max margin (+ (current-column) period))) | 
|---|
| 102 | (forward-char 1)) | 
|---|
| 103 | margin)) | 
|---|
| 104 |  | 
|---|
| 105 | ;;; Add, delete or adjust a C comment box.  If FLAG is nil, the | 
|---|
| 106 | ;;; current boxing style is recognized and preserved.  When 0, the box | 
|---|
| 107 | ;;; is removed; when 1, a single lined box is forced; when 2, a double | 
|---|
| 108 | ;;; lined box is forced; when 3, a Taarna style box is forced.  If | 
|---|
| 109 | ;;; negative, the absolute value is used, but the default style is | 
|---|
| 110 | ;;; changed.  For any other value (like C-u), the default style is | 
|---|
| 111 | ;;; forced.  If REFILL is not nil, refill the comment paragraphs prior | 
|---|
| 112 | ;;; to reboxing. | 
|---|
| 113 |  | 
|---|
| 114 | (defun rebox-c-comment-engine (flag refill) | 
|---|
| 115 | (save-restriction | 
|---|
| 116 | (let ((undo-list buffer-undo-list) | 
|---|
| 117 | (marked-point (point-marker)) | 
|---|
| 118 | (saved-point (point)) | 
|---|
| 119 | box-style left-margin right-margin) | 
|---|
| 120 |  | 
|---|
| 121 | ;; First, find the limits of the block of comments following or | 
|---|
| 122 | ;; enclosing the cursor, or return an error if the cursor is not | 
|---|
| 123 | ;; within such a block of comments, narrow the buffer, and | 
|---|
| 124 | ;; untabify it. | 
|---|
| 125 |  | 
|---|
| 126 | ;; - insure the point is into the following comment, if any | 
|---|
| 127 |  | 
|---|
| 128 | (skip-chars-forward " \t\n") | 
|---|
| 129 | (if (looking-at "/\\*") | 
|---|
| 130 | (forward-char 2)) | 
|---|
| 131 |  | 
|---|
| 132 | (let ((here (point)) start end temp) | 
|---|
| 133 |  | 
|---|
| 134 | ;; - identify a minimal comment block | 
|---|
| 135 |  | 
|---|
| 136 | (search-backward "/*") | 
|---|
| 137 | (setq temp (point)) | 
|---|
| 138 | (beginning-of-line) | 
|---|
| 139 | (setq start (point)) | 
|---|
| 140 | (skip-chars-forward " \t") | 
|---|
| 141 | (if (< (point) temp) | 
|---|
| 142 | (progn | 
|---|
| 143 | (goto-char saved-point) | 
|---|
| 144 | (error "text before comment's start"))) | 
|---|
| 145 | (search-forward "*/") | 
|---|
| 146 | (setq temp (point)) | 
|---|
| 147 | (end-of-line) | 
|---|
| 148 | (if (looking-at "\n") | 
|---|
| 149 | (forward-char 1)) | 
|---|
| 150 | (setq end (point)) | 
|---|
| 151 | (skip-chars-backward " \t\n") | 
|---|
| 152 | (if (> (point) temp) | 
|---|
| 153 | (progn | 
|---|
| 154 | (goto-char saved-point) | 
|---|
| 155 | (error "text after comment's end"))) | 
|---|
| 156 | (if (< end here) | 
|---|
| 157 | (progn | 
|---|
| 158 | (goto-char saved-point) | 
|---|
| 159 | (error "outside any comment block"))) | 
|---|
| 160 |  | 
|---|
| 161 | ;; - try to extend the comment block backwards | 
|---|
| 162 |  | 
|---|
| 163 | (goto-char start) | 
|---|
| 164 | (while (and (not (bobp)) | 
|---|
| 165 | (progn (previous-line 1) | 
|---|
| 166 | (beginning-of-line) | 
|---|
| 167 | (looking-at "[ \t]*/\\*.*\\*/[ \t]*$"))) | 
|---|
| 168 | (setq start (point))) | 
|---|
| 169 |  | 
|---|
| 170 | ;; - try to extend the comment block forward | 
|---|
| 171 |  | 
|---|
| 172 | (goto-char end) | 
|---|
| 173 | (while (looking-at "[ \t]*/\\*.*\\*/[ \t]*$") | 
|---|
| 174 | (forward-line 1) | 
|---|
| 175 | (beginning-of-line) | 
|---|
| 176 | (setq end (point))) | 
|---|
| 177 |  | 
|---|
| 178 | ;; - narrow to the whole block of comments | 
|---|
| 179 |  | 
|---|
| 180 | (narrow-to-region start end)) | 
|---|
| 181 |  | 
|---|
| 182 | ;; Second, remove all the comment marks, and move all the text | 
|---|
| 183 | ;; rigidly to the left to insure the left margin stays at the | 
|---|
| 184 | ;; same place.  At the same time, recognize and save the box | 
|---|
| 185 | ;; style in BOX-STYLE. | 
|---|
| 186 |  | 
|---|
| 187 | (let ((previous-margin (buffer-left-margin)) | 
|---|
| 188 | actual-margin) | 
|---|
| 189 |  | 
|---|
| 190 | ;; - remove all comment marks | 
|---|
| 191 |  | 
|---|
| 192 | (goto-char (point-min)) | 
|---|
| 193 | (replace-regexp "^\\([ \t]*\\)/\\*" "\\1  ") | 
|---|
| 194 | (goto-char (point-min)) | 
|---|
| 195 | (replace-regexp "^\\([ \t]*\\)|" "\\1 ") | 
|---|
| 196 | (goto-char (point-min)) | 
|---|
| 197 | (replace-regexp "\\(\\*/\\||\\)[ \t]*" "") | 
|---|
| 198 | (goto-char (point-min)) | 
|---|
| 199 | (replace-regexp "\\*/[ \t]*/\\*" " ") | 
|---|
| 200 |  | 
|---|
| 201 | ;; - remove the first and last dashed lines | 
|---|
| 202 |  | 
|---|
| 203 | (setq box-style 'plain) | 
|---|
| 204 | (goto-char (point-min)) | 
|---|
| 205 | (if (looking-at "^[ \t]*-*[.\+\\]?[ \t]*\n") | 
|---|
| 206 | (progn | 
|---|
| 207 | (setq box-style 'single) | 
|---|
| 208 | (replace-match "")) | 
|---|
| 209 | (if (looking-at "^[ \t]*=*[.\+\\]?[ \t]*\n") | 
|---|
| 210 | (progn | 
|---|
| 211 | (setq box-style 'double) | 
|---|
| 212 | (replace-match "")))) | 
|---|
| 213 | (goto-char (point-max)) | 
|---|
| 214 | (previous-line 1) | 
|---|
| 215 | (beginning-of-line) | 
|---|
| 216 | (if (looking-at "^[ \t]*[`\+\\]?*[-=]+[ \t]*\n") | 
|---|
| 217 | (progn | 
|---|
| 218 | (if (eq box-style 'plain) | 
|---|
| 219 | (setq box-style 'taarna)) | 
|---|
| 220 | (replace-match ""))) | 
|---|
| 221 |  | 
|---|
| 222 | ;; - remove all spurious whitespace | 
|---|
| 223 |  | 
|---|
| 224 | (goto-char (point-min)) | 
|---|
| 225 | (replace-regexp "[ \t]+$" "") | 
|---|
| 226 | (goto-char (point-min)) | 
|---|
| 227 | (if (looking-at "\n+") | 
|---|
| 228 | (replace-match "")) | 
|---|
| 229 | (goto-char (point-max)) | 
|---|
| 230 | (skip-chars-backward "\n") | 
|---|
| 231 | (if (looking-at "\n\n+") | 
|---|
| 232 | (replace-match "\n")) | 
|---|
| 233 | (goto-char (point-min)) | 
|---|
| 234 | (replace-regexp "\n\n\n+" "\n\n") | 
|---|
| 235 |  | 
|---|
| 236 | ;; - move the text left is adequate | 
|---|
| 237 |  | 
|---|
| 238 | (setq actual-margin (buffer-left-margin)) | 
|---|
| 239 | (if (not (= previous-margin actual-margin)) | 
|---|
| 240 | (indent-rigidly (point-min) (point-max) | 
|---|
| 241 | (- previous-margin actual-margin)))) | 
|---|
| 242 |  | 
|---|
| 243 | ;; Third, select the new box style from the old box style and | 
|---|
| 244 | ;; the argument, choose the margins for this style and refill | 
|---|
| 245 | ;; each paragraph. | 
|---|
| 246 |  | 
|---|
| 247 | ;; - modify box-style only if flag is defined | 
|---|
| 248 |  | 
|---|
| 249 | (if flag | 
|---|
| 250 | (setq box-style | 
|---|
| 251 | (cond ((eq flag 0) 'plain) | 
|---|
| 252 | ((eq flag 1) 'single) | 
|---|
| 253 | ((eq flag 2) 'double) | 
|---|
| 254 | ((eq flag 3) 'taarna) | 
|---|
| 255 | ((eq flag '-) (setq c-box-default-style 'plain) 'plain) | 
|---|
| 256 | ((eq flag -1) (setq c-box-default-style 'single) 'single) | 
|---|
| 257 | ((eq flag -2) (setq c-box-default-style 'double) 'double) | 
|---|
| 258 | ((eq flag -3) (setq c-box-default-style 'taarna) 'taarna) | 
|---|
| 259 | (t c-box-default-style)))) | 
|---|
| 260 |  | 
|---|
| 261 | ;; - compute the left margin | 
|---|
| 262 |  | 
|---|
| 263 | (setq left-margin (buffer-left-margin)) | 
|---|
| 264 |  | 
|---|
| 265 | ;; - temporarily set the fill prefix and column, then refill | 
|---|
| 266 |  | 
|---|
| 267 | (untabify (point-min) (point-max)) | 
|---|
| 268 |  | 
|---|
| 269 | (if refill | 
|---|
| 270 | (let ((fill-prefix (make-string left-margin ? )) | 
|---|
| 271 | (fill-column (- fill-column | 
|---|
| 272 | (if (memq box-style '(single double)) 4 6)))) | 
|---|
| 273 | (fill-region (point-min) (point-max)))) | 
|---|
| 274 |  | 
|---|
| 275 | ;; - compute the right margin after refill | 
|---|
| 276 |  | 
|---|
| 277 | (setq right-margin (buffer-right-margin)) | 
|---|
| 278 |  | 
|---|
| 279 | ;; Fourth, put the narrowed buffer back into a comment box, | 
|---|
| 280 | ;; according to the value of box-style.  Values may be: | 
|---|
| 281 | ;;    plain: insert between a single pair of comment delimiters | 
|---|
| 282 | ;;    single: complete box, overline and underline with dashes | 
|---|
| 283 | ;;    double: complete box, overline and underline with equal signs | 
|---|
| 284 | ;;    taarna: comment delimiters on each line, underline with dashes | 
|---|
| 285 |  | 
|---|
| 286 | ;; - move the right margin to account for left inserts | 
|---|
| 287 |  | 
|---|
| 288 | (setq right-margin (+ right-margin | 
|---|
| 289 | (if (memq box-style '(single double)) | 
|---|
| 290 | 2 | 
|---|
| 291 | 3))) | 
|---|
| 292 |  | 
|---|
| 293 | ;; - construct the box comment, from top to bottom | 
|---|
| 294 |  | 
|---|
| 295 | (goto-char (point-min)) | 
|---|
| 296 | (cond ((eq box-style 'plain) | 
|---|
| 297 |  | 
|---|
| 298 | ;; - construct a plain style comment | 
|---|
| 299 |  | 
|---|
| 300 | (skip-chars-forward " " (+ (point) left-margin)) | 
|---|
| 301 | (insert (make-string (- left-margin (current-column)) ? ) | 
|---|
| 302 | "/* ") | 
|---|
| 303 | (end-of-line) | 
|---|
| 304 | (forward-char 1) | 
|---|
| 305 | (while (not (eobp)) | 
|---|
| 306 | (skip-chars-forward " " (+ (point) left-margin)) | 
|---|
| 307 | (insert (make-string (- left-margin (current-column)) ? ) | 
|---|
| 308 | "   ") | 
|---|
| 309 | (end-of-line) | 
|---|
| 310 | (forward-char 1)) | 
|---|
| 311 | (backward-char 1) | 
|---|
| 312 | (insert "  */")) | 
|---|
| 313 | ((eq box-style 'single) | 
|---|
| 314 |  | 
|---|
| 315 | ;; - construct a single line style comment | 
|---|
| 316 |  | 
|---|
| 317 | (indent-to left-margin) | 
|---|
| 318 | (insert "/*") | 
|---|
| 319 | (insert (make-string (- right-margin (current-column)) ?-) | 
|---|
| 320 | "-.\n") | 
|---|
| 321 | (while (not (eobp)) | 
|---|
| 322 | (skip-chars-forward " " (+ (point) left-margin)) | 
|---|
| 323 | (insert (make-string (- left-margin (current-column)) ? ) | 
|---|
| 324 | "| ") | 
|---|
| 325 | (end-of-line) | 
|---|
| 326 | (indent-to right-margin) | 
|---|
| 327 | (insert " |") | 
|---|
| 328 | (forward-char 1)) | 
|---|
| 329 | (indent-to left-margin) | 
|---|
| 330 | (insert "`") | 
|---|
| 331 | (insert (make-string (- right-margin (current-column)) ?-) | 
|---|
| 332 | "*/\n")) | 
|---|
| 333 | ((eq box-style 'double) | 
|---|
| 334 |  | 
|---|
| 335 | ;; - construct a double line style comment | 
|---|
| 336 |  | 
|---|
| 337 | (indent-to left-margin) | 
|---|
| 338 | (insert "/*") | 
|---|
| 339 | (insert (make-string (- right-margin (current-column)) ?=) | 
|---|
| 340 | "=\\\n") | 
|---|
| 341 | (while (not (eobp)) | 
|---|
| 342 | (skip-chars-forward " " (+ (point) left-margin)) | 
|---|
| 343 | (insert (make-string (- left-margin (current-column)) ? ) | 
|---|
| 344 | "| ") | 
|---|
| 345 | (end-of-line) | 
|---|
| 346 | (indent-to right-margin) | 
|---|
| 347 | (insert " |") | 
|---|
| 348 | (forward-char 1)) | 
|---|
| 349 | (indent-to left-margin) | 
|---|
| 350 | (insert "\\") | 
|---|
| 351 | (insert (make-string (- right-margin (current-column)) ?=) | 
|---|
| 352 | "*/\n")) | 
|---|
| 353 | ((eq box-style 'taarna) | 
|---|
| 354 |  | 
|---|
| 355 | ;; - construct a Taarna style comment | 
|---|
| 356 |  | 
|---|
| 357 | (while (not (eobp)) | 
|---|
| 358 | (skip-chars-forward " " (+ (point) left-margin)) | 
|---|
| 359 | (insert (make-string (- left-margin (current-column)) ? ) | 
|---|
| 360 | "/* ") | 
|---|
| 361 | (end-of-line) | 
|---|
| 362 | (indent-to right-margin) | 
|---|
| 363 | (insert " */") | 
|---|
| 364 | (forward-char 1)) | 
|---|
| 365 | (indent-to left-margin) | 
|---|
| 366 | (insert "/* ") | 
|---|
| 367 | (insert (make-string (- right-margin (current-column)) ?-) | 
|---|
| 368 | " */\n")) | 
|---|
| 369 | (t (error "unknown box style"))) | 
|---|
| 370 |  | 
|---|
| 371 | ;; Fifth, retabify, restore the point position, then cleanup the | 
|---|
| 372 | ;; undo list of any boundary since we started. | 
|---|
| 373 |  | 
|---|
| 374 | ;; - retabify before left margin only (adapted from tabify.el) | 
|---|
| 375 |  | 
|---|
| 376 | (goto-char (point-min)) | 
|---|
| 377 | (while (re-search-forward "^[ \t][ \t][ \t]*" nil t) | 
|---|
| 378 | (let ((column (current-column)) | 
|---|
| 379 | (indent-tabs-mode t)) | 
|---|
| 380 | (delete-region (match-beginning 0) (point)) | 
|---|
| 381 | (indent-to column))) | 
|---|
| 382 |  | 
|---|
| 383 | ;; - restore the point position | 
|---|
| 384 |  | 
|---|
| 385 | (goto-char (marker-position marked-point)) | 
|---|
| 386 |  | 
|---|
| 387 | ;; - remove all intermediate boundaries from the undo list | 
|---|
| 388 |  | 
|---|
| 389 | (if (not (eq buffer-undo-list undo-list)) | 
|---|
| 390 | (let ((cursor buffer-undo-list)) | 
|---|
| 391 | (while (not (eq (cdr cursor) undo-list)) | 
|---|
| 392 | (if (car (cdr cursor)) | 
|---|
| 393 | (setq cursor (cdr cursor)) | 
|---|
| 394 | (rplacd cursor (cdr (cdr cursor)))))))))) | 
|---|
| 395 |  | 
|---|
| 396 | ;;; Rebox a C comment without refilling it. | 
|---|
| 397 |  | 
|---|
| 398 | (defun rebox-c-comment (flag) | 
|---|
| 399 | (interactive "P") | 
|---|
| 400 | (rebox-c-comment-engine flag nil)) | 
|---|
| 401 |  | 
|---|
| 402 | ;;; Rebox a C comment after refilling. | 
|---|
| 403 |  | 
|---|
| 404 | (defun reindent-c-comment (flag) | 
|---|
| 405 | (interactive "P") | 
|---|
| 406 | (rebox-c-comment-engine flag t)) | 
|---|