emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

CC Mode new feature: handling of "null macros".


From: Alan Mackenzie
Subject: CC Mode new feature: handling of "null macros".
Date: Thu, 2 Oct 2014 17:17:51 +0000
User-agent: Mutt/1.5.21 (2010-09-15)

Hello, Daniel.

Some while ago, you expressed dissatisfaction with the fact that in a CC
Mode buffer, "null macros" in certain places could disrupt the proper
fontification (and possibly even indentation) of the code.  As an
example, you cited:

  static int perf_event_period(struct perf_event *event, u64 __user *arg)
                                                             ^^^^^^

, where "__user" got wrongly fontified as the variable instead of "arg".

The solution you suggested was to change the way that language variables
were calculated, allowing a user to change these as a way of
configuration.  In the above case, this would have been
`c-decl-hangon-kwds'.  We had some discussion over this proposal.

I think a better way to solve the problem is to treat these "null
macros" as syntactic whitespace, i.e. have
c-\(forward\|backward\)-syntactic-ws skip over them as though they were
comments.  I have a prototype implementation of this in the patch below.

To use it, set the new (customizable) variables `c-null-macro-names' and
`c-null-macro-with-parens-names' to the Right Thing.  The second of
these is for macros which take an optional parenthetic expression (such
as GCC's "__attribute__"), the first is for macros which never have such
an expression.  In the patch, I have already put "__user" into them.

Would you please try this out and let me know how well it solves the
above problem.  The patch is relative to a very recent Emacs trunk
version.

Thanks in advance!




=== modified file 'lisp/progmodes/cc-defs.el'
*** lisp/progmodes/cc-defs.el   2014-09-10 21:38:11 +0000
--- lisp/progmodes/cc-defs.el   2014-10-02 15:02:56 +0000
***************
*** 584,606 ****
  ;; Wrappers for common scan-lists cases, mainly because it's almost
  ;; impossible to get a feel for how that function works.
  
! (defmacro c-go-list-forward ()
!   "Move backward across one balanced group of parentheses.
  
! Return POINT when we succeed, NIL when we fail.  In the latter case, leave
! point unmoved."
!   `(c-safe (let ((endpos (scan-lists (point) 1 0)))
!            (goto-char endpos)
!            endpos)))
  
! (defmacro c-go-list-backward ()
!   "Move backward across one balanced group of parentheses.
  
! Return POINT when we succeed, NIL when we fail.  In the latter case, leave
! point unmoved."
!   `(c-safe (let ((endpos (scan-lists (point) -1 0)))
!            (goto-char endpos)
!            endpos)))
  
  (defmacro c-up-list-forward (&optional pos limit)
    "Return the first position after the list sexp containing POS,
--- 584,610 ----
  ;; Wrappers for common scan-lists cases, mainly because it's almost
  ;; impossible to get a feel for how that function works.
  
! (defmacro c-go-list-forward (&optional pos limit)
!   "Move backward across one balanced group of parentheses, starting at POS,
! or point when POS is nil or absent.  Return POINT when we succeed, NIL when
! we fail.  In the latter case, leave point unmoved.
  
! A LIMIT for the search may be given.  The start position is assumed to be
! before it."
!   `(let ((res (c-safe-scan-lists ,(or pos `(point)) 1 0 ,limit)))
!      (if res (goto-char res))
!      res))
  
! (defmacro c-go-list-backward (&optional pos limit)
!   "Move backward across one balanced group of parentheses, starting at POS,
! or point when POS is nil or absent.  Return POINT when we succeed, NIL when
! we fail.  In the latter case, leave point unmoved.
  
! A LIMIT for the search may be given.  The start position is assumed to be
! after it."
!   `(let ((res (c-safe-scan-lists ,(or pos '(point)) -1 0 ,limit)))
!      (if res (goto-char res))
!      res))
  
  (defmacro c-up-list-forward (&optional pos limit)
    "Return the first position after the list sexp containing POS,

=== modified file 'lisp/progmodes/cc-engine.el'
*** lisp/progmodes/cc-engine.el 2014-09-11 19:44:25 +0000
--- lisp/progmodes/cc-engine.el 2014-10-02 15:01:56 +0000
***************
*** 1524,1530 ****
  ;;    two newlines with horizontal whitespace between them.
  ;;
  ;;    The reason to include the first following char is to cope with
! ;;    "rung positions" that doesn't have any ordinary whitespace.  If
  ;;    `c-is-sws' is put on a token character it does not have
  ;;    `c-in-sws' set simultaneously.  That's the only case when that
  ;;    can occur, and the reason for not extending the `c-in-sws'
--- 1524,1530 ----
  ;;    two newlines with horizontal whitespace between them.
  ;;
  ;;    The reason to include the first following char is to cope with
! ;;    "rung positions" that don't have any ordinary whitespace.  If
  ;;    `c-is-sws' is put on a token character it does not have
  ;;    `c-in-sws' set simultaneously.  That's the only case when that
  ;;    can occur, and the reason for not extending the `c-in-sws'
***************
*** 1695,1701 ****
      ;; if it's anything that can't start syntactic ws, so we can bail out
      ;; early in the majority of cases when there just are a few ws chars.
      (skip-chars-forward " \t\n\r\f\v")
!     (when (looking-at c-syntactic-ws-start)
  
        (setq rung-end-pos (min (1+ (point)) (point-max)))
        (if (setq rung-is-marked (text-property-any rung-pos rung-end-pos
--- 1695,1703 ----
      ;; if it's anything that can't start syntactic ws, so we can bail out
      ;; early in the majority of cases when there just are a few ws chars.
      (skip-chars-forward " \t\n\r\f\v")
!     (when (or (looking-at c-syntactic-ws-start)
!             (and c-opt-cpp-prefix
!                  (looking-at c-any-null-macro-name-re)))
  
        (setq rung-end-pos (min (1+ (point)) (point-max)))
        (if (setq rung-is-marked (text-property-any rung-pos rung-end-pos
***************
*** 1714,1719 ****
--- 1716,1725 ----
        (with-silent-modifications
        (while
          (progn
+           ;; In the following while form, we move over a "ladder" and
+           ;; following simple WS each time round the loop, appending the WS
+           ;; onto the ladder, joining adjacent ladders, and terminating when
+           ;; there is no more WS or we reach EOB.
            (while
                (when (and rung-is-marked
                           (get-text-property (point) 'c-in-sws))
***************
*** 1757,1762 ****
--- 1763,1769 ----
                            (setq rung-pos (point)
                                  last-put-in-sws-pos rung-pos)))
  
+           ;; Now move over any comments (x)or a CPP construct.
            (setq simple-ws-end (point))
            (c-forward-comments)
  
***************
*** 1782,1787 ****
--- 1789,1809 ----
              (forward-line 1)
              (setq safe-start t)
              ;; Don't cache at eob in case the buffer is narrowed.
+             (not (eobp)))
+ 
+            ((and c-opt-cpp-prefix
+                  (looking-at c-any-null-macro-name-re))
+             ;; Skip over a null macro.
+             (let ((expect-par
+                    (save-match-data
+                     (looking-at c-null-macro-with-parens-name-re)))
+                   pos)
+               (goto-char (match-end 1))
+               (when (and expect-par
+                          (looking-at "[ \t]*\("))
+                 (setq pos (c-up-list-forward (match-end 0) (c-point 'eol)))
+                 (if pos (goto-char pos))))
+             (setq safe-start t)
              (not (eobp)))))
  
        ;; We've searched over a piece of non-white syntactic ws.  See if this
***************
*** 1888,1894 ****
      (when (and (not (bobp))
               (save-excursion
                 (backward-char)
!                (looking-at c-syntactic-ws-end)))
  
        ;; Try to find a rung position in the simple ws preceding point, so that
        ;; we can get a cache hit even if the last bit of the simple ws has
--- 1910,1926 ----
      (when (and (not (bobp))
               (save-excursion
                 (backward-char)
!                (or (looking-at c-syntactic-ws-end)
!                    (and c-opt-cpp-prefix
!                         (looking-at c-symbol-char-key)
!                         (progn (c-beginning-of-current-token)
!                                (looking-at c-any-null-macro-name-re)))
!                    (and c-opt-cpp-prefix
!                         (eq (char-after)?\))
!                         (c-go-up-list-backward nil (c-point 'bol))
!                         (progn (skip-chars-backward " \t")
!                                (c-simple-skip-symbol-backward))
!                         (looking-at c-null-macro-with-parens-name-re)))))
  
        ;; Try to find a rung position in the simple ws preceding point, so that
        ;; we can get a cache hit even if the last bit of the simple ws has
***************
*** 1908,1913 ****
--- 1940,1948 ----
        (with-silent-modifications
        (while
          (progn
+           ;; Each time round the next while form, we move back over a ladder
+           ;; and append any simple WS preceding it, if possible
+           ;; joining with the previous ladder.  ########
            (while
                (when (and rung-is-marked
                           (not (bobp))
***************
*** 2016,2021 ****
--- 2051,2077 ----
              ;; narrowed out, and we can't risk marking the simple ws
              ;; at the end of it.
              (goto-char next-rung-pos)
+             t)
+ 
+            ((and c-opt-cpp-prefix
+                  (eq (char-before) ?\))
+                  (save-excursion
+                    (c-go-list-backward nil (c-point 'bol))
+                    (progn (skip-chars-backward " \t")
+                           (c-simple-skip-symbol-backward)
+                           (setq next-rung-pos (point))
+                           (looking-at c-null-macro-with-parens-name-re))))
+             ;; Skipped over a null macro with a paren expression.
+             (goto-char next-rung-pos)
+             t)
+            ((and c-opt-cpp-prefix
+                  (save-excursion
+                    (and (c-simple-skip-symbol-backward)
+                         (progn (setq next-rung-pos (point))
+                                (looking-at c-any-null-macro-name-re))
+                         )))
+             ;; Skipped over a null macro
+             (goto-char next-rung-pos)
              t)))
  
        ;; We've searched over a piece of non-white syntactic ws.  See if this

=== modified file 'lisp/progmodes/cc-langs.el'
*** lisp/progmodes/cc-langs.el  2014-09-10 21:38:11 +0000
--- lisp/progmodes/cc-langs.el  2014-10-02 15:01:56 +0000
***************
*** 597,602 ****
--- 597,607 ----
    objc (concat c-alnum "_$@"))
  (c-lang-defvar c-symbol-chars (c-lang-const c-symbol-chars))
  
+ (c-lang-defconst c-symbol-char-key
+   "Regexp matching a sequence of at least one identifier character."
+   t (concat "[" (c-lang-const c-symbol-chars) "]+"))
+ (c-lang-defvar c-symbol-char-key (c-lang-const c-symbol-char-key))
+ 
  (c-lang-defconst c-symbol-key
    "Regexp matching identifiers and keywords (with submatch 0).  Assumed
  to match if `c-symbol-start' matches on the same position."

=== modified file 'lisp/progmodes/cc-mode.el'
*** lisp/progmodes/cc-mode.el   2014-09-10 21:38:11 +0000
--- lisp/progmodes/cc-mode.el   2014-10-02 15:01:56 +0000
***************
*** 1344,1349 ****
--- 1344,1350 ----
        abbrev-mode t)
    (use-local-map c-mode-map)
    (c-init-language-vars-for 'c-mode)
+   (c-make-null-macro-regexps)
    (c-make-macro-with-semi-re) ; matches macro names whose expansion ends with 
;
    (c-common-init 'c-mode)
    (easy-menu-add c-c-menu)
***************
*** 1399,1404 ****
--- 1400,1406 ----
        abbrev-mode t)
    (use-local-map c++-mode-map)
    (c-init-language-vars-for 'c++-mode)
+   (c-make-null-macro-regexps)
    (c-make-macro-with-semi-re) ; matches macro names whose expansion ends with 
;
    (c-common-init 'c++-mode)
    (easy-menu-add c-c++-menu)
***************
*** 1452,1457 ****
--- 1454,1460 ----
        abbrev-mode t)
    (use-local-map objc-mode-map)
    (c-init-language-vars-for 'objc-mode)
+   (c-make-null-macro-regexps)
    (c-make-macro-with-semi-re) ; matches macro names whose expansion ends with 
;
    (c-common-init 'objc-mode)
    (easy-menu-add c-objc-menu)

=== modified file 'lisp/progmodes/cc-vars.el'
*** lisp/progmodes/cc-vars.el   2014-01-01 07:43:34 +0000
--- lisp/progmodes/cc-vars.el   2014-10-02 16:41:03 +0000
***************
*** 1613,1618 ****
--- 1613,1672 ----
    :type 'c-extra-types-widget
    :group 'c)
  
+ (defvar c-null-macro-with-parens-name-re nil)
+ (defvar c-null-macro-name-re nil)
+ (defvar c-any-null-macro-name-re nil)
+ 
+ (defcustom c-null-macro-names '("__user")
+   "A list of names of macros which expand to nothing, or compiler extensions
+ like \"????\" which are syntactic noise.  Such a macro/extension is complete 
in
+ itself, never having parentheses.  All these names must be syntactically valid
+ identifiers.
+ 
+ If you change this variable's value, call the function
+ `c-make-null-macro-regexps' to set the necessary internal variables (or do
+ this implicitly by reinitialising C/C++/Objc Mode on any buffer)."
+   :type '(repeat :tag "List of names" string)
+   :group 'c)
+ 
+ (defcustom c-null-macro-with-parens-names '("__attribute__" "__declspec")
+   "A list of names of macros which expand to nothing, or compiler extensions
+ like \"__attribute__\" which are syntactic noise.   All these names must
+ be syntactically valid identifiers.
+ 
+ Such a macro/extension may be followed by a parenthesis expression, but
+ needn't be.  For such an expression to be recognized as part of the null 
macro,
+ it must be entirely on the same line as the macro name, and separated from it,
+ if at all, only by spaces and tabs.
+ 
+ If you change this variable's value, call the function
+ `c-make-null-macro-regexps' to set the necessary internal variables (or do
+ this implicitly by reinitialising C/C++/Objc Mode on any buffer)."
+   :type '(repeat :tag "List of names (possibly empty)" string)
+   :group 'c)
+ 
+ (defun c-make-null-macro-regexps ()
+   ;; Convert `c-null-macro-names' and `c-null-macro-with-parens-names' into
+   ;; `c-any-null-macro-name-re' and `c-null-macro-with-parens-name-re'.
+   (let ((null-m-with-p-re
+        (cond ((null c-null-macro-with-parens-names) "\\<\\>")
+              ((consp c-null-macro-with-parens-names)
+               (regexp-opt c-null-macro-with-parens-names t))
+              (t (error "c-make-null-macro-regexps: \
+ c-null-macro-with-parens-names is invalid: %s" 
c-null-macro-with-parens-names))))
+       (null-m-re
+        (cond ((null c-null-macro-names) "\\<\\>")
+              ((consp c-null-macro-names)
+               (regexp-opt c-null-macro-names t))
+              (t (error "c-make-null-macro-regexps: \
+ c-null-macro-names is invalid: %s" c-null-macro-names)))))
+     (setq c-null-macro-with-parens-name-re
+         (concat null-m-with-p-re "\\([^[:alnum:]_$]\\|$\\)"))
+     (setq c-null-macro-name-re
+         (concat null-m-re "\\([^[:alnum:]_$]\\|$\\)"))
+     (setq c-any-null-macro-name-re
+         (concat "\\(" null-m-with-p-re "\\|"
+                 null-m-re "\\)\\([^[:alnum:]_$]\\|$\\)"))))
  
  ;; Non-customizable variables, still part of the interface to CC Mode
  (defvar c-macro-with-semi-re nil



-- 
Alan Mackenzie (Nuremberg, Germany).



reply via email to

[Prev in Thread] Current Thread [Next in Thread]