]> git.eshelyaron.com Git - emacs.git/commitdiff
DTRT for c-beginning/end-of-defun in nested declaration scopes.
authorAlan Mackenzie <acm@muc.de>
Fri, 28 Oct 2011 14:34:39 +0000 (14:34 +0000)
committerAlan Mackenzie <acm@muc.de>
Fri, 28 Oct 2011 14:34:39 +0000 (14:34 +0000)
doc/misc/cc-mode.texi
lisp/progmodes/cc-cmds.el
lisp/progmodes/cc-engine.el
lisp/progmodes/cc-vars.el

index a9339162666d38ece461a6f9a1c9c94cd624c031..8818904982b48e9e6f8e981aebe2a331815c2cc2 100644 (file)
@@ -882,6 +882,8 @@ lines.
 @itemx @kbd{C-M-e} (@code{c-end-of-defun})
 @findex c-beginning-of-defun
 @findex c-end-of-defun
+@vindex c-defun-tactic
+@vindex defun-tactic (c-)
 
 Move to the beginning or end of the current or next function.  Other
 constructs (such as a structs or classes) which have a brace block
@@ -895,6 +897,15 @@ commands try to leave point at the beginning of a line near the actual
 start or end of the function.  This occasionally causes point not to
 move at all.
 
+By default, these commands will recognize functions contained within a
+@dfn{declaration scope} such as a C++ @code{class} or @code{namespace}
+construct, should the point start inside it.  If @ccmode fails to find
+function beginnings or ends inside the current declaration scope, it
+will search the enclosing scopes.  If you want @ccmode to recognize
+functions only at the top level@footnote{this was @ccmode{}'s
+behaviour prior to version 5.32.}, set @code{c-defun-tatic} to
+@code{t}.
+
 These functions are analogous to the Emacs built-in commands
 @code{beginning-of-defun} and @code{end-of-defun}, except they
 eliminate the constraint that the top-level opening brace of the defun
index 0f873e678c383821bb0e3d6f4b82d220d4946b12..e59fdc16af706a214330e1448ac6626fc063d2cd 100644 (file)
@@ -1485,6 +1485,78 @@ No indentation or other \"electric\" behavior is performed."
       (setq n (1- n))))
    n)
 
+(defun c-narrow-to-most-enclosing-decl-block (&optional inclusive)
+  ;; If we are inside a decl-block (in the sense of c-looking-at-decl-block),
+  ;; i.e. something like namespace{} or extern{}, narrow to the insides of
+  ;; that block (NOT including the enclosing braces) if INCLUSIVE is nil,
+  ;; otherwise include the braces.  If the closing brace is missing,
+  ;; (point-max) is used instead.
+  (let ((paren-state (c-parse-state))
+       encl-decl)
+    (setq encl-decl (and paren-state (c-most-enclosing-decl-block paren-state)))
+    (if encl-decl
+       (save-excursion
+         (narrow-to-region
+          (if inclusive
+              (progn (goto-char encl-decl)
+                     (c-beginning-of-decl-1)
+                     (point))
+            (1+ encl-decl))
+          (progn
+            (goto-char encl-decl)
+            (or (c-safe (forward-list)
+                        (if inclusive
+                            (point)
+                          (1- (point))))
+                (point-max))))))))
+
+(defun c-widen-to-enclosing-decl-scope (paren-state orig-point-min orig-point-max)
+  ;; Narrow the buffer to the innermost declaration scope (e.g. a class, a
+  ;; namespace or the "whole buffer") recorded in PAREN-STATE, the bounding
+  ;; braces NOT being included in the resulting region.  On no account may the
+  ;; final region exceed that bounded by ORIG-POINT-MIN, ORIG-POINT-MAX.
+  ;; PAREN-STATE is a list of buffer positions in the style of
+  ;; (c-parse-state), one of which will be that of the desired opening brace,
+  ;; if there is one.
+  ;;
+  ;; Return the position of the enclosing opening brace, or nil
+  (let (encl-decl)         ; putative position of decl-scope's opening brace.
+    (save-restriction
+      (narrow-to-region orig-point-min orig-point-max)
+      (setq encl-decl (and paren-state
+                          (c-most-enclosing-decl-block paren-state))))
+    (if encl-decl
+       (progn
+         (widen)
+         (narrow-to-region (1+ encl-decl)
+                           (save-excursion
+                             (goto-char encl-decl)
+                             (or (c-safe (forward-list)
+                                         (1- (point)))
+                                 orig-point-max)))
+         encl-decl)
+      (narrow-to-region orig-point-min orig-point-max)
+      nil)))
+
+(eval-and-compile
+  (defmacro c-while-widening-to-decl-block (condition)
+    ;; Repeatedly evaluate CONDITION until it returns nil.  After each
+    ;; evaluation, if `c-defun-tactic' is set appropriately, widen to innards
+    ;; of the next enclosing declaration block (e.g. namespace, class), or the
+    ;; buffer's original restriction.
+    ;;
+    ;; This is a very special purpose macro, which assumes the existence of
+    ;; several variables.  It is for use only in c-beginning-of-defun and
+    ;; c-end-of-defun.
+    `(while
+        (and ,condition
+             (eq c-defun-tactic 'go-outward)
+             lim)
+       (setq paren-state (c-whack-state-after lim paren-state))
+       (setq lim (c-widen-to-enclosing-decl-scope
+                 paren-state orig-point-min orig-point-max))
+       (setq where 'in-block))))
+
 (defun c-beginning-of-defun (&optional arg)
   "Move backward to the beginning of a defun.
 Every top level declaration that contains a brace paren block is
@@ -1509,50 +1581,66 @@ defun."
   (c-save-buffer-state
       (beginning-of-defun-function end-of-defun-function
        (start (point))
-       where paren-state pos)
+       (paren-state (copy-tree (c-parse-state))) ; This must not share list
+                                       ; structure with other users of c-state-cache.
+       (orig-point-min (point-min)) (orig-point-max (point-max))
+       lim                         ; Position of { which has been widened to.
+       where pos)
 
-    ;; Move back out of any macro/comment/string we happen to be in.
-    (c-beginning-of-macro)
-    (setq pos (c-literal-limits))
-    (if pos (goto-char (car pos)))
-
-    (setq where (c-where-wrt-brace-construct))
-
-    (if (< arg 0)
-       ;; Move forward to the closing brace of a function.
-       (progn
-         (if (memq where '(at-function-end outwith-function))
-             (setq arg (1+ arg)))
-         (if (< arg 0)
-             (setq arg (c-forward-to-nth-EOF-} (- arg) where)))
-         ;; Move forward to the next opening brace....
-         (when (and (= arg 0)
-                    (c-syntactic-re-search-forward "{" nil 'eob))
-           (backward-char)
-           ;; ... and backward to the function header.
-           (c-beginning-of-decl-1)
-           t))
-
-      ;; Move backward to the opening brace of a function.
-      (when (and (> arg 0)
-                (eq (setq arg (c-backward-to-nth-BOF-{ arg where)) 0))
+    (save-restriction
+      (if (eq c-defun-tactic 'go-outward)
+         (setq lim (c-widen-to-enclosing-decl-scope ; e.g. class, namespace.
+                    paren-state orig-point-min orig-point-max)))
 
-       ;; Go backward to this function's header.
-       (c-beginning-of-decl-1)
+      ;; Move back out of any macro/comment/string we happen to be in.
+      (c-beginning-of-macro)
+      (setq pos (c-literal-limits))
+      (if pos (goto-char (car pos)))
 
-       (setq pos (point))
-       ;; We're now there, modulo comments and whitespace.
-       ;; Try to be line oriented; position point at the closest
-       ;; preceding boi that isn't inside a comment, but if we hit
-       ;; the previous declaration then we use the current point
-       ;; instead.
-       (while (and (/= (point) (c-point 'boi))
-                   (c-backward-single-comment)))
-       (if (/= (point) (c-point 'boi))
-           (goto-char pos)))
+      (setq where (c-where-wrt-brace-construct))
 
-      (c-keep-region-active)
-      (= arg 0))))
+      (if (< arg 0)
+         ;; Move forward to the closing brace of a function.
+         (progn
+           (if (memq where '(at-function-end outwith-function))
+               (setq arg (1+ arg)))
+           (if (< arg 0)
+               (c-while-widening-to-decl-block
+                (< (setq arg (- (c-forward-to-nth-EOF-} (- arg) where))) 0)))
+           ;; Move forward to the next opening brace....
+           (when (and (= arg 0)
+                      (progn
+                        (c-while-widening-to-decl-block
+                         (not (c-syntactic-re-search-forward "{" nil 'eob)))
+                        (eq (char-before) ?{)))
+             (backward-char)
+             ;; ... and backward to the function header.
+             (c-beginning-of-decl-1)
+             t))
+
+       ;; Move backward to the opening brace of a function, making successively
+       ;; larger portions of the buffer visible as necessary.
+       (when (> arg 0)
+         (c-while-widening-to-decl-block
+          (> (setq arg (c-backward-to-nth-BOF-{ arg where)) 0)))
+
+       (when (eq arg 0)
+         ;; Go backward to this function's header.
+         (c-beginning-of-decl-1)
+
+         (setq pos (point))
+         ;; We're now there, modulo comments and whitespace.
+         ;; Try to be line oriented; position point at the closest
+         ;; preceding boi that isn't inside a comment, but if we hit
+         ;; the previous declaration then we use the current point
+         ;; instead.
+         (while (and (/= (point) (c-point 'boi))
+                     (c-backward-single-comment)))
+         (if (/= (point) (c-point 'boi))
+             (goto-char pos)))
+
+       (c-keep-region-active)
+       (= arg 0)))))
 
 (defun c-forward-to-nth-EOF-} (n where)
   ;; Skip to the closing brace of the Nth function after point.  If
@@ -1617,50 +1705,62 @@ the open-parenthesis that starts a defun; see `beginning-of-defun'."
   (c-save-buffer-state
       (beginning-of-defun-function end-of-defun-function
        (start (point))
-       where paren-state pos)
+       (paren-state (copy-tree (c-parse-state))) ; This must not share list
+                                 ; structure with other users of c-state-cache.
+       (orig-point-min (point-min)) (orig-point-max (point-max))
+       lim
+       where pos)
+    (save-restriction
+      (if (eq c-defun-tactic 'go-outward)
+         (setq lim (c-widen-to-enclosing-decl-scope ; e.g. class, namespace
+                    paren-state orig-point-min orig-point-max)))
 
-    ;; Move back out of any macro/comment/string we happen to be in.
-    (c-beginning-of-macro)
-    (setq pos (c-literal-limits))
-    (if pos (goto-char (car pos)))
+      ;; Move back out of any macro/comment/string we happen to be in.
+      (c-beginning-of-macro)
+      (setq pos (c-literal-limits))
+      (if pos (goto-char (car pos)))
 
-    (setq where (c-where-wrt-brace-construct))
+      (setq where (c-where-wrt-brace-construct))
 
-    (if (< arg 0)
-       ;; Move backwards to the } of a function
-       (progn
-         (if (memq where '(at-header outwith-function))
-             (setq arg (1+ arg)))
-         (if (< arg 0)
-             (setq arg (c-backward-to-nth-BOF-{ (- arg) where)))
-         (if (= arg 0)
-             (c-syntactic-skip-backward "^}")))
-
-      ;; Move forward to the } of a function
-      (if (> arg 0)
-         (setq arg (c-forward-to-nth-EOF-} arg where))))
-
-    ;; Do we need to move forward from the brace to the semicolon?
-    (when (eq arg 0)
-      (if (c-in-function-trailer-p)    ; after "}" of struct/enum, etc.
-         (c-syntactic-re-search-forward ";"))
-
-      (setq pos (point))
-      ;; We're there now, modulo comments and whitespace.
-      ;; Try to be line oriented; position point after the next
-      ;; newline that isn't inside a comment, but if we hit the
-      ;; next declaration then we use the current point instead.
-      (while (and (not (bolp))
-                 (not (looking-at "\\s *$"))
-                 (c-forward-single-comment)))
-      (cond ((bolp))
-           ((looking-at "\\s *$")
-            (forward-line 1))
-           (t
-            (goto-char pos))))
+      (if (< arg 0)
+         ;; Move backwards to the } of a function
+         (progn
+           (if (memq where '(at-header outwith-function))
+               (setq arg (1+ arg)))
+           (if (< arg 0)
+               (c-while-widening-to-decl-block
+                (< (setq arg (- (c-backward-to-nth-BOF-{ (- arg) where))) 0)))
+           (if (= arg 0)
+               (c-while-widening-to-decl-block
+                (progn (c-syntactic-skip-backward "^}")
+                       (not (eq (char-before) ?}))))))
+
+       ;; Move forward to the } of a function
+       (if (> arg 0)
+           (c-while-widening-to-decl-block
+            (> (setq arg (c-forward-to-nth-EOF-} arg where)) 0))))
+
+      ;; Do we need to move forward from the brace to the semicolon?
+      (when (eq arg 0)
+       (if (c-in-function-trailer-p) ; after "}" of struct/enum, etc.
+           (c-syntactic-re-search-forward ";"))
 
-    (c-keep-region-active)
-    (= arg 0)))
+       (setq pos (point))
+       ;; We're there now, modulo comments and whitespace.
+       ;; Try to be line oriented; position point after the next
+       ;; newline that isn't inside a comment, but if we hit the
+       ;; next declaration then we use the current point instead.
+       (while (and (not (bolp))
+                   (not (looking-at "\\s *$"))
+                   (c-forward-single-comment)))
+       (cond ((bolp))
+             ((looking-at "\\s *$")
+              (forward-line 1))
+             (t
+              (goto-char pos))))
+
+      (c-keep-region-active)
+      (= arg 0))))
 
 (defun c-defun-name ()
   "Return the name of the current defun, or NIL if there isn't one.
@@ -1746,6 +1846,11 @@ with a brace block."
   ;;
   ;; This function might do hidden buffer changes.
   (save-excursion
+    (save-restriction
+      (when (eq c-defun-tactic 'go-outward)
+       (c-narrow-to-most-enclosing-decl-block t)  ; e.g. class, namespace
+       (or (save-restriction
+             (c-narrow-to-most-enclosing-decl-block nil)
 
     ;; Note: Some code duplication in `c-beginning-of-defun' and
     ;; `c-end-of-defun'.
@@ -1755,11 +1860,12 @@ with a brace block."
            lim pos end-pos)
        (unless (c-safe
                  (goto-char (c-least-enclosing-brace paren-state))
-                 ;; If we moved to the outermost enclosing paren then we
-                 ;; can use c-safe-position to set the limit.  Can't do
-                 ;; that otherwise since the earlier paren pair on
-                 ;; paren-state might very well be part of the
-                 ;; declaration we should go to.
+                           ;; If we moved to the outermost enclosing paren
+                           ;; then we can use c-safe-position to set the
+                           ;; limit. Can't do that otherwise since the
+                           ;; earlier paren pair on paren-state might very
+                           ;; well be part of the declaration we should go
+                           ;; to.
                  (setq lim (c-safe-position (point) paren-state))
                  t)
          ;; At top level.  Make sure we aren't inside a literal.
@@ -1843,8 +1949,12 @@ with a brace block."
                         (forward-line 1)
                         (point))
                        (t
-                        pos)))))
-       ))))
+                                  pos))))))))
+           (and (not near)
+                (goto-char (point-min))
+                (c-forward-decl-or-cast-1 -1 nil nil)
+                (eq (char-after) ?\{)
+                (cons (point-min) (point-max))))))))
 
 (defun c-mark-function ()
   "Put mark at end of the current top-level declaration or macro, point at beginning.
index b2c548847c34f02f1955a5cc8f023b2ca04c0c0c..bc42e1032ab972caa30d75fc469ed93c333b0ffb 100644 (file)
@@ -705,7 +705,7 @@ comment at the start of cc-engine.el for more info."
        ;; The last position where a label is possible provided the
        ;; statement started there.  It's nil as long as no invalid
        ;; label content has been found (according to
-       ;; `c-nonlabel-token-key'.  It's `start' if no valid label
+       ;; `c-nonlabel-token-key').  It's `start' if no valid label
        ;; content was found in the label.  Note that we might still
        ;; regard it a label if it starts with `c-label-kwds'.
        label-good-pos
@@ -1035,7 +1035,12 @@ comment at the start of cc-engine.el for more info."
                  ;; (including a case label) or something like C++'s "public:"?
                  ;; A case label might use an expression rather than a token.
                  (setq after-case:-pos (or tok start))
-                 (if (looking-at c-nonlabel-token-key) ; e.g. "while" or "'a'"
+                 (if (or (looking-at c-nonlabel-token-key) ; e.g. "while" or "'a'"
+                         ;; Catch C++'s inheritance construct "class foo : bar".
+                         (save-excursion
+                           (and
+                            (c-safe (c-backward-sexp) t)
+                            (looking-at c-nonlabel-token-2-key))))
                      (setq c-maybe-labelp nil)
                    (if after-labels-pos ; Have we already encountered a label?
                        (if (not last-label-pos)
@@ -8037,6 +8042,29 @@ comment at the start of cc-engine.el for more info."
          (back-to-indentation)
          (vector (point) open-paren-pos))))))
 
+(defmacro c-pull-open-brace (ps)
+  ;; Pull the next open brace from PS (which has the form of paren-state),
+  ;; skipping over any brace pairs.  Returns NIL when PS is exhausted.
+  `(progn
+     (while (consp (car ,ps))
+       (setq ,ps (cdr ,ps)))
+     (prog1 (car ,ps)
+       (setq ,ps (cdr ,ps)))))
+
+(defun c-most-enclosing-decl-block (paren-state)
+  ;; Return the buffer position of the most enclosing decl-block brace (in the
+  ;; sense of c-looking-at-decl-block) in the PAREN-STATE structure, or nil if
+  ;; none was found.
+  (let* ((open-brace (c-pull-open-brace paren-state))
+        (next-open-brace (c-pull-open-brace paren-state)))
+    (while (and open-brace
+               (save-excursion
+                 (goto-char open-brace)
+                 (not (c-looking-at-decl-block next-open-brace nil))))
+      (setq open-brace next-open-brace
+           next-open-brace (c-pull-open-brace paren-state)))
+    open-brace))
+
 (defun c-inside-bracelist-p (containing-sexp paren-state)
   ;; return the buffer position of the beginning of the brace list
   ;; statement if we're inside a brace list, otherwise return nil.
index 58dc1737c5a8b14514eb747030c2f32d7f4417db..769bf63f63c95366b0a2a90da909b57993162d67 100644 (file)
@@ -340,6 +340,20 @@ better with the \"do { ... } while \(0)\" trick)."
   :group 'c)
 (put 'c-syntactic-indentation-in-macros 'safe-local-variable 'booleanp)
 
+(defcustom c-defun-tactic 'go-outward
+  "*Whether functions are recognized inside, e.g., a class.
+This is used by `c-beginning-of-defun' and like functions.
+
+Its value is one of:
+ t           -- Functions are recognized only at the top level.
+ go-outward  -- Nested functions are also recognized.  Should a function
+                command hit the beginning/end of a nested scope, it will
+                carry on at the less nested level."
+  :type '(radio
+         (const :tag "Functions are at the top-level" t)
+         (const :tag "Functions are also recognized inside declaration scopes" go-outward))
+  :group 'c)
+
 (defcustom-c-stylevar c-comment-only-line-offset 0
   "*Extra offset for line which contains only the start of a comment.
 Can contain an integer or a cons cell of the form: