From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Alan Mackenzie Newsgroups: gmane.emacs.devel Subject: CC Mode new feature: handling of "null macros". Date: Thu, 2 Oct 2014 17:17:51 +0000 Message-ID: <20141002171751.GA4238@acm.acm> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: ger.gmane.org 1412270665 19419 80.91.229.3 (2 Oct 2014 17:24:25 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Thu, 2 Oct 2014 17:24:25 +0000 (UTC) Cc: emacs-devel@gnu.org To: Daniel Colascione Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Thu Oct 02 19:24:16 2014 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1XZk6y-0005G4-Ib for ged-emacs-devel@m.gmane.org; Thu, 02 Oct 2014 19:24:16 +0200 Original-Received: from localhost ([::1]:35403 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XZk6y-0002BM-6c for ged-emacs-devel@m.gmane.org; Thu, 02 Oct 2014 13:24:16 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:49749) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XZk6d-00028K-Bb for emacs-devel@gnu.org; Thu, 02 Oct 2014 13:24:02 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1XZk6S-0001L0-4Y for emacs-devel@gnu.org; Thu, 02 Oct 2014 13:23:55 -0400 Original-Received: from colin.muc.de ([193.149.48.1]:12403 helo=mail.muc.de) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XZk6R-0001I7-3f for emacs-devel@gnu.org; Thu, 02 Oct 2014 13:23:44 -0400 Original-Received: (qmail 59643 invoked by uid 3782); 2 Oct 2014 17:23:32 -0000 Original-Received: from acm.muc.de (pD951A872.dip0.t-ipconnect.de [217.81.168.114]) by colin.muc.de (tmda-ofmipd) with ESMTP; Thu, 02 Oct 2014 19:23:30 +0200 Original-Received: (qmail 5162 invoked by uid 1000); 2 Oct 2014 17:17:51 -0000 Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) X-Delivery-Agent: TMDA/1.1.12 (Macallan) X-Primary-Address: acm@muc.de X-detected-operating-system: by eggs.gnu.org: FreeBSD 8.x X-Received-From: 193.149.48.1 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:174943 Archived-At: 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).