Function: c-mask-paragraph
c-mask-paragraph is a byte-compiled function defined in cc-cmds.el.gz.
Signature
(c-mask-paragraph FILL-PARAGRAPH APPLY-OUTSIDE-LITERAL FUN &rest ARGS)
Source Code
;; Defined in /usr/src/emacs/lisp/progmodes/cc-cmds.el.gz
(defun c-mask-paragraph (fill-paragraph apply-outside-literal fun &rest args)
;; Calls FUN with ARGS ar arguments while the current paragraph is
;; masked to allow adaptive filling to work correctly. That
;; includes narrowing the buffer and, if point is inside a comment,
;; masking the comment starter and ender appropriately.
;;
;; FILL-PARAGRAPH is non-nil if called for whole paragraph filling.
;; The position of point is then less significant when doing masking
;; and narrowing.
;;
;; If APPLY-OUTSIDE-LITERAL is nil then the function will be called
;; only if the point turns out to be inside a comment or a string.
;;
;; Note that this function does not do any hidden buffer changes.
(let (fill
;; beg and end limit the region to narrow. end is a marker.
beg end
;; tmp-pre and tmp-post mark strings that are temporarily
;; inserted at the start and end of the region. tmp-pre is a
;; cons of the positions of the prepended string. tmp-post is
;; a marker pointing to the single character of the appended
;; string.
tmp-pre tmp-post
;; If hang-ender-stuck isn't nil, the comment ender is
;; hanging. In that case it's set to the number of spaces
;; that should be between the text and the ender.
hang-ender-stuck
;; auto-fill-spaces is the exact sequence of whitespace between a
;; comment's last word and the comment ender, temporarily replaced
;; with 'x's before calling FUN when FILL-PARAGRAPH is nil.
auto-fill-spaces
(here (point))
(c-lit-limits c-lit-limits)
(c-lit-type c-lit-type))
;; Restore point on undo. It's necessary since we do a lot of
;; hidden inserts and deletes below that should be as transparent
;; as possible.
(if (and buffer-undo-list (not (eq buffer-undo-list t)))
(setq buffer-undo-list (cons (point) buffer-undo-list)))
;; Determine the limits and type of the containing literal (if any):
;; C-LIT-LIMITS, C-LIT-TYPE; and the limits of the current paragraph:
;; BEG and END.
(c-save-buffer-state ()
(save-restriction
;; Widen to catch comment limits correctly.
(widen)
(unless c-lit-limits
(setq c-lit-limits (c-literal-limits nil fill-paragraph)))
(setq c-lit-limits (c-collect-line-comments c-lit-limits))
(unless c-lit-type
(setq c-lit-type (c-literal-type c-lit-limits))))
(save-excursion
(unless (c-safe (backward-char)
(forward-paragraph)
(>= (point) here))
(goto-char here)
(forward-paragraph))
(setq end (point-marker)))
(save-excursion
(unless (c-safe (forward-char)
(backward-paragraph)
(<= (point) here))
(goto-char here)
(backward-paragraph))
(setq beg (point))))
(unwind-protect
(progn
;; For each of the possible types of text (string, C comment ...)
;; determine BEG and END, the region we will narrow to. If we're in
;; a literal, constrain BEG and END to the limits of this literal.
;;
;; For some of these text types, particularly a block comment, we
;; may need to massage whitespace near literal delimiters, so that
;; these don't get filled inappropriately.
(cond
((eq c-lit-type 'c++) ; Line comment.
(save-excursion
;; Limit to the comment or paragraph end, whichever
;; comes first.
(set-marker end (min end (cdr c-lit-limits)))
(when (<= beg (car c-lit-limits))
;; The region includes the comment starter, so we must
;; check it.
(goto-char (car c-lit-limits))
(back-to-indentation)
(if (eq (point) (car c-lit-limits))
;; Include the first line in the region.
(setq beg (c-point 'bol))
;; The first line contains code before the
;; comment. We must fake a line that doesn't.
(setq tmp-pre t))))
(setq apply-outside-literal t))
((eq c-lit-type 'c) ; Block comment.
(when
(or (> end (cdr c-lit-limits))
(and (= end (cdr c-lit-limits))
(eq (char-before end) ?/)
(eq (char-before (1- end)) ?*)
;; disallow "/*/"
(> (- (cdr c-lit-limits) (car c-lit-limits)) 3)))
;; There is a comment ender, and the region includes it. If
;; it's on its own line, it stays on its own line. If it's got
;; company on the line, it keeps (at least one word of) it.
;; "=====*/" counts as a comment ender here, but "===== */"
;; doesn't and "foo*/" doesn't.
(unless
(save-excursion
(goto-char (cdr c-lit-limits))
(beginning-of-line)
;; The following conjunct was added to avoid an
;; "Invalid search bound (wrong side of point)"
;; error in the subsequent re-search. Maybe
;; another fix would be needed (2007-12-08).
; (or (<= (- (cdr c-lit-limits) 2) (point))
; 2010-10-17 Construct removed.
; (or (< (- (cdr c-lit-limits) 2) (point))
(and
(search-forward-regexp
(concat "\\=[ \t]*\\(" c-current-comment-prefix "\\)")
(- (cdr c-lit-limits) 2) t)
(not (search-forward-regexp
"\\(\\s \\|\\sw\\)"
(- (cdr c-lit-limits) 2) 'limit))
;; The comment ender IS on its own line. Exclude this
;; line from the filling.
(set-marker end (c-point 'bol))));)
;; The comment ender is hanging. Replace all space between it
;; and the last word either by one or two 'x's (when
;; FILL-PARAGRAPH is non-nil), or a row of x's the same width
;; as the whitespace (when auto filling), and include it in
;; the region. We'll change them back to whitespace
;; afterwards. The effect of this is to glue the comment
;; ender to the last word in the comment during filling.
(let* ((ender-start (save-excursion
(goto-char (cdr c-lit-limits))
(skip-syntax-backward "^w ")
(point)))
(ender-column (save-excursion
(goto-char ender-start)
(current-column)))
(point-rel (- ender-start here))
(sentence-ends-comment
(save-excursion
(goto-char ender-start)
(and (search-backward-regexp
(c-sentence-end) (c-point 'bol) t)
(goto-char (match-end 0))
(looking-at "[ \t]*")
(= (match-end 0) ender-start))))
spaces)
(save-excursion
;; Insert a CR after the "*/", adjust END
(goto-char (cdr c-lit-limits))
(setq tmp-post (point-marker))
(insert ?\n)
(set-marker end (point))
(forward-line -1) ; last line of the comment
(if (and (looking-at (concat "[ \t]*\\(\\("
c-current-comment-prefix
"\\)[ \t]*\\)"))
(eq ender-start (match-end 0)))
;; The comment ender is prefixed by nothing but a
;; comment line prefix. IS THIS POSSIBLE? (ACM,
;; 2006/4/28). Remove it along with surrounding ws.
(setq spaces (- (match-end 1) (match-end 2)))
(goto-char ender-start))
(skip-chars-backward " \t\r\n") ; Surely this can be
; " \t"? "*/" is NOT alone on the line (ACM, 2005/8/18)
;; What's being tested here? 2006/4/20. FIXME!!!
(if (/= (point) ender-start)
(progn
(if (<= here (point))
;; Don't adjust point below if it's
;; before the string we replace.
(setq point-rel -1))
;; Keep one or two spaces between the
;; text and the ender, depending on how
;; many there are now.
(unless spaces
(setq spaces (- ender-column (current-column))))
(setq auto-fill-spaces (c-delete-and-extract-region
(point) ender-start))
;; paragraph filling condenses multiple spaces to
;; single or double spaces. auto-fill doesn't.
(if fill-paragraph
(setq spaces
(max
(min spaces
(if (and sentence-ends-comment
sentence-end-double-space)
2 1))
1)))
;; Insert the filler first to keep marks right.
(insert-char ?x spaces t)
(setq hang-ender-stuck spaces)
(setq point-rel
(and (>= point-rel 0)
(- (point) (min point-rel spaces)))))
(setq point-rel nil)))
(if point-rel
;; Point was in the middle of the string we
;; replaced above, so put it back in the same
;; relative position, counting from the end.
(goto-char point-rel)))
))
(when (<= beg (car c-lit-limits))
;; The region includes the comment starter.
(save-excursion
(goto-char (car c-lit-limits))
(if (looking-at (concat "\\(" comment-start-skip "\\)$"))
;; Begin with the next line.
(setq beg (c-point 'bonl))
;; Fake the fill prefix in the first line.
(setq tmp-pre t))))
(setq apply-outside-literal t))
((eq c-lit-type 'string) ; String.
(save-excursion
(when (>= end (cdr c-lit-limits))
(goto-char (1- (cdr c-lit-limits)))
(setq tmp-post (point-marker))
(insert ?\n)
(set-marker end (point)))
(when (<= beg (car c-lit-limits))
(goto-char (1+ (car c-lit-limits)))
(setq beg (if (looking-at "\\\\$")
;; Leave the start line if it's
;; nothing but an escaped newline.
(1+ (match-end 0))
(point)))))
(setq apply-outside-literal t))
((eq c-lit-type 'pound) ; Macro
;; Narrow to the macro limits if they are nearer than the
;; paragraph limits. Don't know if this is necessary but
;; do it for completeness sake (doing auto filling at all
;; inside macros is bogus to begin with since the line
;; continuation backslashes aren't handled).
(save-excursion
(c-save-buffer-state ()
(c-beginning-of-macro)
(beginning-of-line)
(if (> (point) beg)
(setq beg (point)))
(c-end-of-macro)
(forward-line)
(if (< (point) end)
(set-marker end (point))))))
(t ; Other code.
;; Try to avoid comments and macros in the paragraph to
;; avoid that the adaptive fill mode gets the prefix from
;; them.
(c-save-buffer-state nil
(save-excursion
(goto-char beg)
(c-forward-syntactic-ws end)
(beginning-of-line)
(setq beg (point))
(goto-char end)
(c-backward-syntactic-ws beg)
(forward-line)
(set-marker end (point))))))
(when tmp-pre
;; Temporarily insert the fill prefix after the comment
;; starter so that the first line looks like any other
;; comment line in the narrowed region.
(setq fill (c-save-buffer-state nil
(c-guess-fill-prefix c-lit-limits c-lit-type)))
(unless (string-match (concat "\\`[ \t]*\\("
c-current-comment-prefix
"\\)[ \t]*\\'")
(car fill))
;; Oops, the prefix doesn't match the comment prefix
;; regexp. This could produce very confusing
;; results with adaptive fill packages together with
;; the insert prefix magic below, since the prefix
;; often doesn't appear at all. So let's warn about
;; it.
(message "\
Warning: Regexp from `c-comment-prefix-regexp' doesn't match the comment prefix %S"
(car fill)))
;; Find the right spot on the line, break it, insert
;; the fill prefix and make sure we're back in the
;; same column by temporarily prefixing the first word
;; with a number of 'x'.
(save-excursion
(goto-char (car c-lit-limits))
(if (looking-at (if (eq c-lit-type 'c++)
c-current-comment-prefix
comment-start-skip))
(goto-char (match-end 0))
(forward-char 2)
(skip-chars-forward " \t"))
(while (and (< (current-column) (cdr fill))
(not (eolp)))
(forward-char 1))
(let ((col (current-column)))
(setq beg (1+ (point))
tmp-pre (list (point)))
(unwind-protect
(progn
(insert-and-inherit "\n" (car fill))
(insert-char ?x (- col (current-column)) t))
(setcdr tmp-pre (point))))))
(when apply-outside-literal
;; `apply-outside-literal' is always set to t here if
;; we're inside a literal.
(let ((fill-prefix
(or fill-prefix
;; Kludge: If the function that adapts the fill prefix
;; doesn't produce the required comment starter for
;; line comments, then force it by setting fill-prefix.
(when (and (eq c-lit-type 'c++)
;; Kludge the kludge: filladapt-mode doesn't
;; have this problem, but it currently
;; doesn't override fill-context-prefix
;; (version 2.12).
(not (and (boundp 'filladapt-mode)
filladapt-mode))
(not (string-match
"\\`[ \t]*//"
(or (fill-context-prefix beg end)
""))))
(c-save-buffer-state nil
(car (or fill (c-guess-fill-prefix
c-lit-limits c-lit-type)))))))
;; Save the relative position of point if it's outside the
;; region we're going to narrow. Want to restore it in that
;; case, but otherwise it should be moved according to the
;; called function.
(point-rel (cond ((< (point) beg) (- (point) beg))
((> (point) end) (- (point) end)))))
;; Preparations finally done! Now we can call the
;; actual function.
(prog1
(save-restriction
(narrow-to-region beg end)
(apply fun args))
(if point-rel
;; Restore point if it was outside the region.
(if (< point-rel 0)
(goto-char (+ beg point-rel))
(goto-char (+ end point-rel))))))))
(when (consp tmp-pre)
(delete-region (car tmp-pre) (cdr tmp-pre)))
(when tmp-post
(save-excursion
(goto-char tmp-post)
(delete-char 1))
(when hang-ender-stuck
;; Preserve point even if it's in the middle of the string
;; we replace; save-excursion doesn't work in that case.
(setq here (point))
(goto-char tmp-post)
(skip-syntax-backward "^w ")
(forward-char (- hang-ender-stuck))
(if (or fill-paragraph (not auto-fill-spaces))
(insert-char ?\ hang-ender-stuck t)
(insert auto-fill-spaces))
(delete-char hang-ender-stuck)
(goto-char here))
(set-marker tmp-post nil))
(set-marker end nil))))