]> code.delx.au - gnu-emacs/blobdiff - lisp/progmodes/c-mode.el
(c-indent-region): As first thing, advance to a nonblank line.
[gnu-emacs] / lisp / progmodes / c-mode.el
index ec4b2c646ab951472d9d231f255a1db046e30e43..429d0bf2292959e1632936a8dd8873a82c63bcf2 100644 (file)
@@ -48,6 +48,9 @@
   (define-key c-mode-map "\ea" 'c-beginning-of-statement)
   (define-key c-mode-map "\ee" 'c-end-of-statement)
   (define-key c-mode-map "\eq" 'c-fill-paragraph)
+  (define-key c-mode-map "\C-c\C-n" 'c-forward-conditional)
+  (define-key c-mode-map "\C-c\C-p" 'c-backward-conditional)
+  (define-key c-mode-map "\C-c\C-u" 'c-up-conditional)
   (define-key c-mode-map "\177" 'backward-delete-char-untabify)
   (define-key c-mode-map "\t" 'c-indent-command))
 
@@ -544,6 +547,7 @@ Return the amount the indentation changed by."
                  (setq indent (save-excursion
                                 (forward-char)
                                 (backward-sexp)
+                                (c-backward-to-start-of-if)
                                 (current-indentation))))
                 ((and (looking-at "while\\b")
                       (save-excursion
@@ -638,6 +642,8 @@ Returns nil if line starts inside a string, t if in a comment."
                                     ;; Make sure the "function decl" we found
                                     ;; is not inside a comment.
                                     (progn
+                                      ;; Move back to the `(' starting arglist
+                                      (goto-char lim)
                                       (beginning-of-line)
                                       (while (and (not comment)
                                                   (search-forward "/*" lim t))
@@ -880,7 +886,7 @@ Otherwise return nil and don't move point."
 (defun c-beginning-of-statement (count)
   "Go to the beginning of the innermost C statement.
 With prefix arg, go back N - 1 statements.  If already at the beginning of a
-statement then go to the beginning of the preceeding one.
+statement then go to the beginning of the preceding one.
 If within a string or comment, or next to a comment (only whitespace between),
 move by sentences instead of statements."
   (interactive "p")
@@ -951,10 +957,10 @@ If within a string or comment, move by sentences instead of statements."
   (beginning-of-defun)
   (backward-paragraph))
 \f
+;; Idea of ENDPOS is, indent each line, stopping when
+;; ENDPOS is encountered.  But it's too much of a pain to make that work.
 (defun indent-c-exp (&optional endpos)
-  "Indent each line of the C grouping following point.
-If optional arg ENDPOS is given, indent each line, stopping when
-ENDPOS is encountered."
+  "Indent each line of the C grouping following point."
   (interactive)
   (let* ((indent-stack (list nil))
         (opoint (point))  ;; May be altered below.
@@ -965,13 +971,15 @@ ENDPOS is encountered."
                      (save-excursion (forward-char 1)
                                      (beginning-of-defun)
                                      (setq funbeg (point)))
+                     (setq opoint funbeg)
                      ;; Try to find containing open,
                      ;; but don't scan past that fcn-start.
                      (save-restriction
                        (narrow-to-region funbeg (point))
                        (condition-case nil
                            (save-excursion
-                             (backward-up-list 1) (point))
+                             (backward-up-list 1)
+                             (point))
                          ;; We gave up: must be between fcns.
                          ;; Set opoint to beg of prev fcn
                          ;; since otherwise calculate-c-indent
@@ -983,7 +991,7 @@ ENDPOS is encountered."
         restart outer-loop-done inner-loop-done state ostate
         this-indent last-sexp
         at-else at-brace at-while
-        last-depth
+        last-depth this-point
         (next-depth 0))
     ;; If the braces don't match, get an error right away.
     (save-excursion
@@ -994,6 +1002,12 @@ ENDPOS is encountered."
        (and (re-search-forward
              comment-start-skip
              (save-excursion (end-of-line) (point)) t)
+            ;; Make sure this isn't a comment alone on a line
+            ;; (which should be indented like code instead).
+            (save-excursion
+              (goto-char (match-beginning 0))
+              (skip-chars-backward " \t")
+              (not (bolp)))
             ;; Make sure the comment starter we found
             ;; is not actually in a string or quoted.
             (let ((new-state
@@ -1021,9 +1035,12 @@ ENDPOS is encountered."
          (if (and (car (cdr (cdr state)))
                   (>= (car (cdr (cdr state))) 0))
              (setq last-sexp (car (cdr (cdr state)))))
-         (if (or (nth 4 ostate))
+         ;; If this line started within a comment, indent it as such.
+         (if (or (nth 4 ostate) (nth 7 ostate))
              (c-indent-line))
-         (if (or (nth 3 state))
+         ;; If it ends outside of comments or strings, exit the inner loop.
+         ;; Otherwise move on to next line.
+         (if (or (nth 3 state) (nth 4 state) (nth 7 state))
              (forward-line 1)
            (setq inner-loop-done t)))
        (and endpos
@@ -1056,7 +1073,12 @@ ENDPOS is encountered."
                                                        (point)))))
          (forward-line 1)
          (skip-chars-forward " \t")
-         (if (eolp)
+         ;; Don't really reindent if the line is just whitespace,
+         ;; or if it is past the endpos.
+         ;; (The exit test in the outer while
+         ;; does not exit until we have passed the first line
+         ;; past the region.)
+         (if (or (eolp) (and endpos (>= (point) endpos)))
              nil
            (if (and (car indent-stack)
                     (>= (car indent-stack) 0))
@@ -1068,6 +1090,7 @@ ENDPOS is encountered."
                  ;; Is it a new statement?  Is it an else?
                  ;; Find last non-comment character before this line
                  (save-excursion
+                   (setq this-point (point))
                    (setq at-else (looking-at "else\\W"))
                    (setq at-brace (= (following-char) ?{))
                    (setq at-while (looking-at "while\\b"))
@@ -1088,6 +1111,9 @@ ENDPOS is encountered."
                                                  (current-indentation))))
                            ((and at-while (c-backward-to-start-of-do opoint))
                             (setq this-indent (current-indentation)))
+                           ((eq (preceding-char) ?\,)
+                            (goto-char this-point)
+                            (setq this-indent (calculate-c-indent)))
                            (t (setq this-indent (car indent-stack)))))))
              ;; Just started a new nesting level.
              ;; Compute the standard indent for this level.
@@ -1095,6 +1121,10 @@ ENDPOS is encountered."
                           (if (car indent-stack)
                               (- (car indent-stack))
                             opoint))))
+               ;; t means we are in a block comment and should
+               ;; calculate accordingly.
+               (if (eq val t)
+                   (setq val (calculate-c-indent-within-comment)))
                (setcar indent-stack
                        (setq this-indent val))))
            ;; Adjust line indentation according to its contents
@@ -1107,7 +1137,16 @@ ENDPOS is encountered."
            (if (= (following-char) ?})
                (setq this-indent (- this-indent c-indent-level)))
            (if (= (following-char) ?{)
-               (setq this-indent (+ this-indent c-brace-offset)))
+               ;; Don't move an open-brace in column 0.
+               ;; This is good when constructs such as
+               ;; `extern "C" {' surround a function definition
+               ;; that should be indented as usual.
+               ;; It is also good for nested functions.
+               ;; It is bad when an open-brace is indented at column 0
+               ;; and you want to fix that, but we can't win 'em all.
+               (if (zerop (current-column))
+                   (setq this-indent 0)
+                 (setq this-indent (+ this-indent c-brace-offset))))
            ;; Don't leave indentation in empty lines.
            (if (eolp) (setq this-indent 0))
            ;; Put chosen indentation into effect.
@@ -1152,10 +1191,46 @@ ENDPOS is encountered."
 (defun c-indent-region (start end)
   (save-excursion
     (goto-char start)
-    (let ((endmark (copy-marker end)))
-      (and (bolp) (not (eolp))
-          (c-indent-line))
-      (indent-c-exp endmark)
+    ;; Advance to first nonblank line.
+    (skip-chars-forward " \t\n")
+    (beginning-of-line)
+    (let ((endmark (copy-marker end))
+         (c-tab-always-indent t))
+      (while (and (bolp) (not (eolp)))
+       ;; Indent one line as with TAB.
+       (let ((shift-amt (c-indent-line))
+             nextline sexpbeg sexpend)
+         (save-excursion
+           ;; Find beginning of following line.
+           (save-excursion
+             (forward-line 1) (setq nextline (point)))
+           ;; Find first beginning-of-sexp for sexp extending past this line.
+           (beginning-of-line)
+           (while (< (point) nextline)
+             (condition-case nil
+                 (progn
+                   (forward-sexp 1)
+                   (setq sexpend (point-marker)))
+               (error (setq sexpend nil)
+                      (goto-char nextline)))
+             (skip-chars-forward " \t\n"))
+           (if sexpend
+               (progn
+                 ;; Make sure the sexp we found really starts on the
+                 ;; current line and extends past it.
+                 (goto-char sexpend)
+                 (backward-sexp 1)
+                 (setq sexpbeg (point)))))
+         ;; If that sexp ends within the region,
+         ;; indent it all at once, fast.
+         (if (and sexpend (> sexpend nextline) (<= sexpend endmark)
+                  (< sexpbeg nextline))
+             (progn
+               (indent-c-exp)
+               (goto-char sexpend)))
+         ;; Move to following line and try again.
+         (and sexpend (set-marker sexpend nil))
+         (forward-line 1)))
       (set-marker endmark nil))))
 \f
 (defun set-c-style (style &optional global)
@@ -1251,15 +1326,28 @@ move forward to the end of the containing preprocessor conditional.
 When going backwards, `#elif' is treated like `#else' followed by `#if'.
 When going forwards, `#elif' is ignored."
   (interactive "p")
-  (let* ((forward (< count 0))
+  (c-forward-conditional (- count) t))
+
+(defun c-backward-conditional (count &optional up-flag)
+  "Move back across a preprocessor conditional, leaving mark behind.
+A prefix argument acts as a repeat count.  With a negative argument,
+move forward across a preprocessor conditional."
+  (interactive "p")
+  (c-forward-conditional (- count) up-flag))
+
+(defun c-forward-conditional (count &optional up-flag)
+  "Move forward across a preprocessor conditional, leaving mark behind.
+A prefix argument acts as a repeat count.  With a negative argument,
+move backward across a preprocessor conditional."
+  (interactive "p")
+  (let* ((forward (> count 0))
         (increment (if forward -1 1))
         (search-function (if forward 're-search-forward 're-search-backward))
         (opoint (point))
         (new))
     (save-excursion
       (while (/= count 0)
-       (if forward (end-of-line))
-       (let ((depth 0) found)
+       (let ((depth (if up-flag 0 -1)) found)
          (save-excursion
            ;; Find the "next" significant line in the proper direction.
            (while (and (not found)
@@ -1275,7 +1363,7 @@ When going forwards, `#elif' is ignored."
              (beginning-of-line)
              ;; Now verify it is really a preproc line.
              (if (looking-at "^[ \t]*#[ \t]*\\(if\\|elif\\|endif\\)")
-                 (progn
+                 (let ((prev depth))
                    ;; Update depth according to what we found.
                    (beginning-of-line)
                    (cond ((looking-at "[ \t]*#[ \t]*endif")
@@ -1284,16 +1372,22 @@ When going forwards, `#elif' is ignored."
                           (if (and forward (= depth 0))
                               (setq found (point))))
                          (t (setq depth (- depth increment))))
+                   ;; If we are trying to move across, and we find
+                   ;; an end before we find a beginning, get an error.
+                   (if (and (< prev 0) (< depth prev))
+                       (error (if forward
+                                  "No following conditional at this level"
+                                "No previous conditional at this level")))
+                   ;; When searching forward, start from next line
+                   ;; so that we don't find the same line again.
+                   (if forward (forward-line 1))
                    ;; If this line exits a level of conditional, exit inner loop.
                    (if (< depth 0)
-                       (setq found (point)))
-                   ;; When searching forward, start from end of line
-                   ;; so that we don't find the same line again.
-                   (if forward (end-of-line))))))
+                       (setq found (point)))))))
          (or found
              (error "No containing preprocessor conditional"))
          (goto-char (setq new found)))
-       (setq count (- count increment))))
+       (setq count (+ count increment))))
     (push-mark)
     (goto-char new)))