[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).
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- CC Mode new feature: handling of "null macros".,
Alan Mackenzie <=