unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* POC: customizable cc-mode keywords
@ 2014-05-02  5:26 Daniel Colascione
  2014-05-10 23:13 ` Daniel Colascione
  2014-05-11 21:13 ` Alan Mackenzie
  0 siblings, 2 replies; 41+ messages in thread
From: Daniel Colascione @ 2014-05-02  5:26 UTC (permalink / raw)
  To: Emacs developers

[-- Attachment #1: Type: text/plain, Size: 18192 bytes --]

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.

=== 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 @@
 \f
 ;;; 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 file.
+                                             (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)))
+
 \f
 (cc-provide 'cc-defs)


=== 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.
 ;; 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 variables
-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))

 \f
 (cc-provide 'cc-langs)

=== 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.

=== 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)

 \f
+
+(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)
+
+\f
 ;; 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 expansion



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 884 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2014-09-26 19:19 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-05-02  5:26 POC: customizable cc-mode keywords Daniel Colascione
2014-05-10 23:13 ` Daniel Colascione
2014-05-11 21:13 ` Alan Mackenzie
2014-05-11 21:23   ` Daniel Colascione
2014-05-16 17:52     ` Alan Mackenzie
2014-05-16 18:06       ` Daniel Colascione
2014-05-18 21:33         ` Alan Mackenzie
2014-05-18 22:28           ` Daniel Colascione
2014-05-19  2:25             ` Stefan Monnier
2014-05-25 18:08             ` Alan Mackenzie
2014-09-08 17:28           ` Stefan Monnier
2014-09-11 13:55             ` Further CC-mode changes Stefan Monnier
2014-09-12 23:59               ` Alan Mackenzie
2014-09-13  1:09                 ` Ivan Andrus
2014-09-13 10:04                   ` Alan Mackenzie
2014-09-13  3:04                 ` Stefan Monnier
2014-09-13 15:10                   ` Alan Mackenzie
2014-09-13 19:24                     ` Stefan Monnier
2014-09-13 23:08                       ` Syntax-propertize and CC-mode [Was: Further CC-mode changes] Alan Mackenzie
2014-09-14  4:04                         ` Stefan Monnier
2014-09-16 17:30                       ` Sync'ing cc-mode Stefan Monnier
2014-09-26 19:19                         ` Stefan Monnier
2014-09-15 20:24                     ` Further CC-mode changes Glenn Morris
2014-09-16  3:07                       ` Stephen J. Turnbull
2014-09-16 13:39                         ` Stefan Monnier
2014-09-16 14:22                         ` David Kastrup
2014-09-16 23:40                           ` Stephen J. Turnbull
2014-09-17  1:02                             ` Stefan Monnier
2014-09-17  1:48                               ` Stephen J. Turnbull
2014-09-17  5:22                                 ` David Kastrup
2014-09-17 13:00                                   ` Stefan Monnier
2014-09-17 18:31                               ` Glenn Morris
2014-09-17 19:12                                 ` David Kastrup
2014-09-17  5:24                             ` Eli Zaretskii
2014-09-17  6:54                               ` Stephen J. Turnbull
2014-09-17  7:20                                 ` Eli Zaretskii
2014-09-17  7:30                                 ` David Kastrup
2014-09-17 13:04                                 ` Stefan Monnier
2014-09-17 18:25                                   ` Glenn Morris
2014-09-18  5:20                                   ` Stephen J. Turnbull
2014-09-18  9:44                             ` Emilio Lopes

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).