From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Daniel Colascione Newsgroups: gmane.emacs.devel Subject: POC: customizable cc-mode keywords Date: Thu, 01 May 2014 22:26:07 -0700 Message-ID: <53632C6F.5070903@dancol.org> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="4GXUi7Wb8IqodVnspj8RAF2bO5ogljCVl" X-Trace: ger.gmane.org 1399008393 25364 80.91.229.3 (2 May 2014 05:26:33 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Fri, 2 May 2014 05:26:33 +0000 (UTC) To: Emacs developers Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Fri May 02 07:26:27 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 1Wg5zL-0003mJ-4c for ged-emacs-devel@m.gmane.org; Fri, 02 May 2014 07:26:23 +0200 Original-Received: from localhost ([::1]:42310 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Wg5zK-0000ln-Nr for ged-emacs-devel@m.gmane.org; Fri, 02 May 2014 01:26:22 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:59615) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Wg5zG-0000lQ-Ak for emacs-devel@gnu.org; Fri, 02 May 2014 01:26:20 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Wg5zE-0007AQ-0Q for emacs-devel@gnu.org; Fri, 02 May 2014 01:26:18 -0400 Original-Received: from dancol.org ([2600:3c01::f03c:91ff:fedf:adf3]:58509) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Wg5zD-000788-9e for emacs-devel@gnu.org; Fri, 02 May 2014 01:26:15 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=dancol.org; s=x; h=Content-Type:Subject:To:MIME-Version:From:Date:Message-ID; bh=0hcUyS0Oi/fYefBtbz91qaj0bmDIN3fs8b9EexzvD6o=; b=FV8/ACRSm2HFIUvZUzIgzx3KKuTHzm/QQnyWkFB5lKfc7K4qFueDzKA4FdGUAOx3KJOcZjWxemsAdcocaLzv9HpZzX3Yw60MBLUMsiIh0yrsiFhUdQN/syivjy87sc0EzeEHcvnUKbpjBUfnruNZ5ZEnExW0Yn9Wv+yRQNjZwJ7muUbq19byRVjuKq72b2tGc3EzltepfntXE24cdrpOeI3rS1TNdQwPNaoUCAH0eh58GONaHuj9woBAEYNMhr2B2J2DLQcvx8OK6REQAP+cGDGPYiM7FJbOiVZITtokY3KMOOYDLTTWTgk5AToTahYwvhDmH2CSA5cl+8m/zQNffQ==; Original-Received: from [2601:8:b200:2b6::2b1] by dancol.org with esmtpsa (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.82) (envelope-from ) id 1Wg5z6-0001QV-NW for emacs-devel@gnu.org; Thu, 01 May 2014 22:26:08 -0700 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Thunderbird/24.5.0 X-Enigmail-Version: 1.6 X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2600:3c01::f03c:91ff:fedf:adf3 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:171666 Archived-At: This is an OpenPGP/MIME signed message (RFC 4880 and 3156) --4GXUi7Wb8IqodVnspj8RAF2bO5ogljCVl Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable cc-mode has trouble with parsing dialects of C that use the preprocessor heavily. Consider this example from the Linux kernel: static int perf_event_period(struct perf_event *event, u64 __user *arg)= __user is defined to some GCC static analysis nonsense, but since cc-mode doesn't know that, we see __user fontified in font-lock-variable-name-face and *arg untouched. This example is fairly benign (if ugly), but there are other cases where variations in pre-processor C dialect confuse cc-mode in larger regions, leading to odd fontification and indentation. The patch below adds customizable options for additional C-family language "keywords". To add this feature, we have to change how cc-mode evaluates its language variables. Today, we use clever macros to hard-code the values of all cc-mode language variables into the mode functions of each cc-mode major mode function or into c-init-language-vars-for, but in order to allow users to customize cc-mode syntax, we have to be able to recompute language constants and variables at runtime. The new code simply evaluates cc-mode language setter forms at mode initialization instead. This approach is slower, but not by much: it takes 0.9ms to set up cc-mode's ~130 language variables using the precompiled function approach, while it takes 1.6ms to do the same work using dynamic evaluation. I can live with this performance regression. As implemented, the keyword list can only be customized globally, but it'd be nice to be able to do something buffer-local too. =3D=3D=3D modified file 'lisp/progmodes/cc-defs.el' --- lisp/progmodes/cc-defs.el 2014-02-09 12:34:25 +0000 +++ lisp/progmodes/cc-defs.el 2014-05-02 04:47:35 +0000 @@ -89,7 +89,7 @@ =0C ;;; Variables also used at compile time. -(defconst c-version "5.32.5" +(defconst c-version "5.32.5.1" "CC Mode version number.") (defconst c-version-sym (intern c-version)) @@ -1812,8 +1812,6 @@ ;; and other miscellaneous data. The obarray might also contain ;; various other symbols, but those don't have any variable bindings. -(defvar c-lang-const-expansion nil) - (defsubst c-get-current-file () ;; Return the base name of the current file. (let ((file (cond @@ -1880,19 +1878,6 @@ constant. A file is identified by its base name." (let* ((sym (intern (symbol-name name) c-lang-constants)) - ;; Make `c-lang-const' expand to a straightforward call to - ;; `c-get-lang-constant' in `cl-macroexpand-all' below. - ;; - ;; (The default behavior, i.e. to expand to a call inside - ;; `eval-when-compile' should be equivalent, since that macro - ;; should only expand to its content if it's used inside a - ;; form that's already evaluated at compile time. It's - ;; however necessary to use our cover macro - ;; `cc-eval-when-compile' due to bugs in `eval-when-compile', - ;; and it expands to a bulkier form that in this case only is - ;; unnecessary garbage that we don't want to store in the - ;; language constant source definitions.) - (c-lang-const-expansion 'call) (c-langs-are-parametric t) bindings pre-files) @@ -2037,53 +2022,28 @@ "Unknown language %S since it got no `c-mode-prefix' property" (symbol-name lang)))) - (if (eq c-lang-const-expansion 'immediate) - ;; No need to find out the source file(s) when we evaluate - ;; immediately since all the info is already there in the - ;; `source' property. - `',(c-get-lang-constant name nil mode) - - (let ((file (c-get-current-file))) - (if file (setq file (intern file))) - ;; Get the source file(s) that must be loaded to get the value - ;; of the constant. If the symbol isn't defined yet we assume - ;; that its definition will come later in this file, and thus - ;; are no file dependencies needed. - (setq source-files (nreverse - ;; Reverse to get the right load order. - (apply 'nconc - (mapcar (lambda (elem) - (if (eq file (car elem)) - nil ; Exclude our own file. - (list (car elem)))) - (get sym 'source)))))) - - ;; Make some effort to do a compact call to - ;; `c-get-lang-constant' since it will be compiled in. - (setq args (and mode `(',mode))) - (if (or source-files args) - (setq args (cons (and source-files `',source-files) - args))) - - (if (or (eq c-lang-const-expansion 'call) - (and (not c-lang-const-expansion) - (not mode)) - load-in-progress - (not (boundp 'byte-compile-dest-file)) - (not (stringp byte-compile-dest-file))) - ;; Either a straight call is requested in the context, or - ;; we're in an "uncontrolled" context and got no language, - ;; or we're not being byte compiled so the compile time - ;; stuff below is unnecessary. - `(c-get-lang-constant ',name ,@args) - - ;; Being compiled. If the loading and compiling version is - ;; the same we use a value that is evaluated at compile time, - ;; otherwise it's evaluated at runtime. - `(if (eq c-version-sym ',c-version-sym) - (cc-eval-when-compile - (c-get-lang-constant ',name ,@args)) - (c-get-lang-constant ',name ,@args)))))) + (let ((file (c-get-current-file))) + (if file (setq file (intern file))) + ;; Get the source file(s) that must be loaded to get the value + ;; of the constant. If the symbol isn't defined yet we assume + ;; that its definition will come later in this file, and thus + ;; are no file dependencies needed. + (setq source-files (nreverse + ;; Reverse to get the right load order. + (apply 'nconc + (mapcar (lambda (elem) + (if (eq file (car elem)) + nil ; Exclude our own fil= e. + (list (car elem)))) + (get sym 'source)))))) + + ;; Make some effort to do a compact call to `c-get-lang-constant' + ;; and omit unneeded arguments since this code will be compiled. + (setq args (and mode `(',mode))) + (if (or source-files args) + (setq args (cons (and source-files `',source-files) + args))) + `(c-get-lang-constant ',name ,@args))) (defvar c-lang-constants-under-evaluation nil) @@ -2262,6 +2222,18 @@ (setq buf-mode (get buf-mode 'c-fallback-mode)))) match)) +(defun c-clear-value-cache () + "Forget already-computed `c-lang-defvar' values. +Call this function to make changes to cc-mode language +variables take effect at the next mode initialization." + ;; Clear cached constant values + (mapatoms (lambda (sym) + (set sym nil)) + c-lang-constants) + ;; Recompute our font lock keyword constants + (when (featurep 'cc-fonts) + (load "cc-fonts" nil t))) + =0C (cc-provide 'cc-defs) =3D=3D=3D modified file 'lisp/progmodes/cc-langs.el' --- lisp/progmodes/cc-langs.el 2014-01-01 07:43:34 +0000 +++ lisp/progmodes/cc-langs.el 2014-05-02 05:19:24 +0000 @@ -1921,15 +1921,13 @@ ;; declaration. Specifically, they aren't recognized in the middle ;; of multi-token types, inside declarators, and between the ;; identifier and the arglist paren of a function declaration. - ;; - ;; FIXME: This ought to be user customizable since compiler stuff - ;; like this usually is wrapped in project specific macros. (It'd - ;; of course be even better if we could cope without knowing this.) - t nil - (c c++) '(;; GCC extension. - "__attribute__" - ;; MSVC extension. - "__declspec")) + t (when (boundp (c-mode-symbol "extra-keywords")) + (mapcar #'car (c-mode-var "extra-keywords"))) + (c c++) (append (c-lang-const c-decl-hangon-kwds) + '( ;; GCC extension. + "__attribute__" + ;; MSVC extension. + "__declspec"))) (c-lang-defconst c-decl-hangon-key ;; Adorned regexp matching `c-decl-hangon-kwds'. @@ -2120,11 +2118,18 @@ (c-lang-defconst c-paren-nontype-kwds "Keywords that may be followed by a parenthesis expression that doesn'= t contain type identifiers." - t nil - (c c++) '(;; GCC extension. - "__attribute__" - ;; MSVC extension. - "__declspec")) + t (when (boundp (c-mode-symbol "extra-keywords")) + (apply 'nconc + (mapcar (lambda (kw) + (when (cdr kw) + (list (car kw)))) + (c-mode-var "extra-keywords")))) + (c c++) (append + (c-lang-const c-paren-nontype-kwds) + '( ;; GCC extension. + "__attribute__" + ;; MSVC extension. + "__declspec"))) (c-lang-defconst c-paren-type-kwds "Keywords that may be followed by a parenthesis expression containing @@ -3155,115 +3160,38 @@ ;; Make the `c-lang-setvar' variables buffer local in the current buffer= =2E ;; These are typically standard emacs variables such as `comment-start'.= -(defmacro c-make-emacs-variables-local () - `(progn - ,@(mapcar (lambda (init) - `(make-local-variable ',(car init))) - (cdr c-emacs-variable-inits)))) - -(defun c-make-init-lang-vars-fun (mode) - "Create a function that initializes all the language dependent variabl= es -for the given mode. - -This function should be evaluated at compile time, so that the -function it returns is byte compiled with all the evaluated results -from the language constants. Use the `c-init-language-vars' macro to -accomplish that conveniently." - - (if (and (not load-in-progress) - (boundp 'byte-compile-dest-file) - (stringp byte-compile-dest-file)) - - ;; No need to byte compile this lambda since the byte compiler is - ;; smart enough to detect the `funcall' construct in the - ;; `c-init-language-vars' macro below and compile it all straight - ;; into the function that contains `c-init-language-vars'. - `(lambda () - - ;; This let sets up the context for `c-mode-var' and similar - ;; that could be in the result from `cl-macroexpand-all'. - (let ((c-buffer-is-cc-mode ',mode) - current-var source-eval) - (c-make-emacs-variables-local) - (condition-case err - - (if (eq c-version-sym ',c-version-sym) - (setq ,@(let ((c-buffer-is-cc-mode mode) - (c-lang-const-expansion 'immediate)) - ;; `c-lang-const' will expand to the evaluated - ;; constant immediately in `cl-macroexpand-all' - ;; below. - (mapcan - (lambda (init) - `(current-var ',(car init) - ,(car init) ,(cl-macroexpand-all - (elt init 1)))) - ;; Note: The following `append' copies the - ;; first argument. That list is small, so - ;; this doesn't matter too much. - (append (cdr c-emacs-variable-inits) - (cdr c-lang-variable-inits))))) - - ;; This diagnostic message isn't useful for end - ;; users, so it's disabled. - ;;(unless (get ',mode 'c-has-warned-lang-consts) - ;; (message ,(concat "%s compiled with CC Mode %s " - ;; "but loaded with %s - evaluating " - ;; "language constants from source") - ;; ',mode ,c-version c-version) - ;; (put ',mode 'c-has-warned-lang-consts t)) - - (setq source-eval t) - (let ((init ',(append (cdr c-emacs-variable-inits) - (cdr c-lang-variable-inits)))) - (while init - (setq current-var (caar init)) - (set (caar init) (eval (cadar init))) - (setq init (cdr init))))) - - (error - (if current-var - (message "Eval error in the `c-lang-defvar' or `c-lang-setvar' for `%s'%s: %S" - current-var - (if source-eval - (format "\ - (fallback source eval - %s compiled with CC Mode %s but loaded with %s)= " - ',mode ,c-version c-version) - "") - err) - (signal (car err) (cdr err))))))) - - ;; Being evaluated from source. Always use the dynamic method to - ;; work well when `c-lang-defvar's in this file are reevaluated - ;; interactively. - `(lambda () - (require 'cc-langs) - (let ((c-buffer-is-cc-mode ',mode) - (init (append (cdr c-emacs-variable-inits) - (cdr c-lang-variable-inits))) - current-var) - (c-make-emacs-variables-local) - (condition-case err - - (while init - (setq current-var (caar init)) - (set (caar init) (eval (cadar init))) - (setq init (cdr init))) - - (error - (if current-var - (message - "Eval error in the `c-lang-defvar' or `c-lang-setver' for `%s' (source eval): %S" - current-var err) - (signal (car err) (cdr err))))))) - )) +(defun c-make-emacs-variables-local () + (mapcar (lambda (init) + (make-local-variable (car init))) + (cdr c-emacs-variable-inits))) + +(defun c-init-language-vars-for (mode) + "Initialize the cc-mode language variables for MODE. +MODE is a symbol naming the mode to initialize." + (let ((c-buffer-is-cc-mode mode) + (init (append (cdr c-emacs-variable-inits) + (cdr c-lang-variable-inits))) + current-var) + (c-make-emacs-variables-local) + (condition-case err + (while init + (setq current-var (caar init)) + (set (caar init) (eval (cadar init) nil)) + (setq init (cdr init))) + (error + (if current-var + (message + "Eval error in the `c-lang-defvar' or `c-lang-setver' for `%s' (source eval): %S" + current-var err) + (signal (car err) (cdr err))))))) (defmacro c-init-language-vars (mode) "Initialize all the language dependent variables for the given mode. -This macro is expanded at compile time to a form tailored for the mode -in question, so MODE must be a constant. Therefore MODE is not -evaluated and should not be quoted." - `(funcall ,(c-make-init-lang-vars-fun mode))) +MODE is not evaluated and should not be quoted. This macro used +to produce an optimized initialization tailored to MODE, but that +optimization is no longer worth it. Use +`c-init-language-vars-for' instead." + `(c-init-language-vars-for ',mode)) =0C (cc-provide 'cc-langs) =3D=3D=3D modified file 'lisp/progmodes/cc-mode.el' --- lisp/progmodes/cc-mode.el 2014-03-04 04:03:34 +0000 +++ lisp/progmodes/cc-mode.el 2014-05-02 01:20:44 +0000 @@ -149,21 +149,6 @@ (defun c-leave-cc-mode-mode () (setq c-buffer-is-cc-mode nil)) -(defun c-init-language-vars-for (mode) - "Initialize the language variables for one of the language modes -directly supported by CC Mode. This can be used instead of the -`c-init-language-vars' macro if the language you want to use is one of -those, rather than a derived language defined through the language -variable system (see \"cc-langs.el\")." - (cond ((eq mode 'c-mode) (c-init-language-vars c-mode)) - ((eq mode 'c++-mode) (c-init-language-vars c++-mode)) - ((eq mode 'objc-mode) (c-init-language-vars objc-mode)) - ((eq mode 'java-mode) (c-init-language-vars java-mode)) - ((eq mode 'idl-mode) (c-init-language-vars idl-mode)) - ((eq mode 'pike-mode) (c-init-language-vars pike-mode)) - ((eq mode 'awk-mode) (c-init-language-vars awk-mode)) - (t (error "Unsupported mode %s" mode)))) - ;;;###autoload (defun c-initialize-cc-mode (&optional new-style-init) "Initialize CC Mode for use in the current buffer. =3D=3D=3D 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-05-02 05:24:28 +0000 @@ -1614,6 +1614,75 @@ :group 'c) =0C + +(define-widget 'c-extra-keywords-widget 'lazy + "Internal CC Mode widget for the `*-extra-keywords' variables." + :type '(repeat + (cons + (string :tag "Keyword") + (boolean :tag "Parenthesized expression follows")))) + +(defun c-make-extra-keywords-blurb (mode1 mode2) + (concat "\ +*List of extra keywords to recognize in " + mode1 " mode. +Each list item should be a cons (KW . PAREN). +KW should be a string naming a single identifier. +PAREN should be nil or t. If t, expect the a parenthesized expression +after KW and skip over it. + +Note that this variable is only consulted when the major mode is +initialized. If you change it later you have to reinitialize CC +Mode by doing \\[" mode2 "]. Additionally, if you change this +variable outside of customize, you need to call +`c-clear-value-cache' to make your changes take effect.")) + +(defun c-extra-keywords-setter (sym val) + (set-default sym val) + (c-clear-value-cache)) + +(defcustom c-extra-keywords + nil + (c-make-extra-keywords-blurb "C" "c-mode") + :type 'c-extra-keywords-widget + :set 'c-extra-keywords-setter + :group 'c) + +(defcustom c++-extra-keywords + nil + (c-make-extra-keywords-blurb "C++" "c++-mode") + :type 'c-extra-keywords-widget + :set 'c-extra-keywords-setter + :group 'c) + +(defcustom objc-extra-keywords + nil + (c-make-extra-keywords-blurb "ObjC" "objc-mode") + :type 'c-extra-keywords-widget + :set 'c-extra-keywords-setter + :group 'c) + +(defcustom java-extra-keywords + nil + (c-make-extra-keywords-blurb "Java" "java-mode") + :type 'c-extra-keywords-widget + :set 'c-extra-keywords-setter + :group 'c) + +(defcustom idl-extra-keywords nil + nil + :type 'c-extra-keywords-widget + :set 'c-extra-keywords-setter + :group 'c) + +(defcustom pike-extra-keywords + nil + (c-make-extra-keywords-blurb "Pike" "pike-mode") + :type 'c-extra-keywords-widget + :set 'c-extra-keywords-setter + :group 'c) + +=0C ;; Non-customizable variables, still part of the interface to CC Mode (defvar c-macro-with-semi-re nil ;; Regular expression which matches a (#define'd) symbol whose expansi= on --4GXUi7Wb8IqodVnspj8RAF2bO5ogljCVl Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="signature.asc" -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/ iQIcBAEBAgAGBQJTYyxvAAoJEMAaIROpHW7IUboP/ikuqd36yhfuAQ4v8xHIqawu fatFNVoW2/fscT1Q6/JXXCv7iH+6bf3tl0Y1gVrUUeihf/JtwEc+P3vNp7qZvoZn kFPrpwzNCaSaaGiA06bTdsAcud8sYbe4NWbaOjQLqJ1GRASQMciySP+TyfIJL6nH O3nWf3MY4175Cxc+pkdsaGYzwpiKDcS0F5FXaRBL8Zq7UaKT2xns5mrHHabBJtWw HUsXS2/mJzkm7MtZBROLDTfpOhK02ZkFOiReNWrWb4Rt3GTL+uE+/5FcoUpKcxKf ULmESyQbkGV77YtGHdIze3dx1+XIJ3nUv2VBFGQc+ZhofuTSZ/d3vcI4Kgqh31ME OImaOxSGJiAxf2zLH5eXf4jJq+MjiBTccaOKeAVBSNr25ZiljjieGRUxivaZuAn/ TxpH66R7k7Nf8h6efDwoeTI/EOVZn1609QmvZg8HoCfUA6GqFucp9KaVmVX0+GII v6c8WBNuzZb56P71w91OqGh8Vn0b5MwdRq+tMPHbslkHur1iUL8/GaL/Cn/jM58M zeqJYNLnA1n5NaM7BKvKDDVjVfEzHPNI3hMxYAXn0dxjsuvgPWM38o9xt3zgGdEp p58cr6mKyP3VBR5vUEX6ntDoMa3pCvXVwvnl66H+O0sLEFYbb+grYsTqK2Z6TK06 B4AGJMhk1Y6tDsCpAFJw =OgN+ -----END PGP SIGNATURE----- --4GXUi7Wb8IqodVnspj8RAF2bO5ogljCVl--