Subject: [PATCH] CC-modes hierarchy

From: Stefan Monnier <[email-masked]>
Date: Fri, 11 Sep 2015 22:32:00 -0400

Package: Emacs
Version: 25.0.50

Any objection to the patch below?

It does the following:
- Move code common to all CC-mode major modes to a c-mode-common-mode function.
- Add new c-derivative-mode as a parent of C, C++, and ObjC, for
  settings which apply to C-derived languages but not for others.
  This has become necessary given that CC-mode is used nowadays as an
  engine for modes which have little to do with C.
- Remove code that's redundant with what define-derived-mode does:
  - set local-map
  - set syntax-table
  - set keymap parent
- Remove c-make-inherited-keymap, since it's not used any more.
- Fix c-after-font-lock-init so it only *moves* the function, and doesn't
  accidentally add it (in case the derived mode decided to remove the function
  from the hook, for example).

AFAIK this patch has no issues w.r.t compatibility since it relies on
behavior of define-derived-mode which has existed since "for ever".
But it hasn't seen much testing, admittedly (except for my own personal use).

        Stefan

diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el
index 97491e4..4279c9f 100644
--- a/lisp/progmodes/cc-mode.el
+++ b/lisp/progmodes/cc-mode.el
@@ -71,6 +71,17 @@
 ;;
 ;;    http://lists.sourceforge.net/mailman/listinfo/cc-mode-announce
 
+;; Externally maintained major modes which use CC-mode's engine:
+;; - cuda-mode
+;; - haxe-mode
+;; - d-mode
+;; - dart-mode
+;; - cc-php-js-cs.el
+;; - php-mode
+;; - yang-mode
+;; - math-mode (mathematica)
+;; - unrealscript-mode
+
 ;;; Code:
 
 ;; For Emacs < 22.2.
@@ -200,22 +211,6 @@ control).  See \"cc-mode.el\" for more info."
 (defvar c-mode-base-map ()
   "Keymap shared by all CC Mode related modes.")
 
-(defun c-make-inherited-keymap ()
-  (let ((map (make-sparse-keymap)))
-    ;; Necessary to use `cc-bytecomp-fboundp' below since this
-    ;; function is called from top-level forms that are evaluated
-    ;; while cc-bytecomp is active when one does M-x eval-buffer.
-    (cond
-     ;; Emacs
-     ((cc-bytecomp-fboundp 'set-keymap-parent)
-      (set-keymap-parent map c-mode-base-map))
-     ;; XEmacs
-     ((fboundp 'set-keymap-parents)
-      (set-keymap-parents map c-mode-base-map))
-     ;; incompatible
-     (t (error "CC Mode is incompatible with this version of Emacs")))
-    map))
-
 (defun c-define-abbrev-table (name defs &optional doc)
   ;; Compatibility wrapper for `define-abbrev' which passes a non-nil
   ;; sixth argument for SYSTEM-FLAG in emacsen that support it
@@ -1219,7 +1214,7 @@ Note that the style variables are always made local to the buffer."
       (backward-char))			; back over (, [, <.
     (and (/= new-pos pos) new-pos)))
 
-(defun c-change-expand-fl-region (beg end old-len)
+(defun c-change-expand-fl-region (_beg _end _old-len)
   ;; Expand the region (c-new-BEG c-new-END) to an after-change font-lock
   ;; region.  This will usually be the smallest sequence of whole lines
   ;; containing `c-new-BEG' and `c-new-END', but if `c-new-BEG' is in a
@@ -1307,8 +1302,9 @@ Note that the style variables are always made local to the buffer."
 (defun c-after-font-lock-init ()
   ;; Put on `font-lock-mode-hook'.  This function ensures our after-change
   ;; function will get executed before the font-lock one.
-  (remove-hook 'after-change-functions 'c-after-change t)
-  (add-hook 'after-change-functions 'c-after-change nil t))
+  (when (memq #'c-after-change after-change-functions)
+    (remove-hook 'after-change-functions #'c-after-change t)
+    (add-hook 'after-change-functions #'c-after-change nil t)))
 
 (defun c-font-lock-init ()
   "Set up the font-lock variables for using the font-lock support in CC Mode.
@@ -1401,6 +1397,27 @@ This function is called from `c-common-init', once per mode initialization."
     (c-update-modeline)))
 
 
+(defvar c-mode-common-map c-mode-base-map)
+
+(define-derived-mode c-mode-common prog-mode "CC-generic"
+  "Pseudo major mode, parent of all modes using the CC engine."
+  (c-initialize-cc-mode t)
+  (setq abbrev-mode t))                  ;FIXME: Why?
+
+(defvar c-derivative-mode-map
+  ;; FIXME: We can't have the menu on this keymap, because the menus for C,
+  ;; C++, and ObjC can't be shared: the only difference between them is their
+  ;; title, but easy-menu offers no way to compute the title dynamically.
+  (let ((map (make-sparse-keymap)))
+    ;; Add bindings which are useful for any C derivative.
+    (define-key map "\C-c\C-e"  #'c-macro-expand)
+    map)
+  "Keymap used in c-derivative-mode buffers.")
+
+(define-derived-mode c-derivative-mode c-mode-common "C-derivative"
+  "Pseudo major mode, parent of all modes for C derivatives.
+A C derivative is a language which is a superset of C (or is C itself).")
+
 ;; Support for C
 
 (defvar c-mode-syntax-table
@@ -1413,9 +1430,8 @@ This function is called from `c-common-init', once per mode initialization."
   "Abbreviation table used in c-mode buffers.")
 
 (defvar c-mode-map
-  (let ((map (c-make-inherited-keymap)))
+  (let ((map (make-sparse-keymap)))
     ;; Add bindings which are only useful for C.
-    (define-key map "\C-c\C-e"  'c-macro-expand)
     map)
   "Keymap used in c-mode buffers.")
 
@@ -1454,7 +1470,7 @@ This function is called from `c-common-init', once per mode initialization."
 (unless (fboundp 'prog-mode) (defalias 'prog-mode 'fundamental-mode))
 
 ;;;###autoload
-(define-derived-mode c-mode prog-mode "C"
+(define-derived-mode c-mode c-derivative-mode "C"
   "Major mode for editing K&R and ANSI C code.
 To submit a problem report, enter `\\[c-submit-bug-report]' from a
 c-mode buffer.  This automatically sets up a mail buffer with version
@@ -1468,11 +1484,6 @@ initialization, then `c-mode-hook'.
 
 Key bindings:
 \\{c-mode-map}"
-  (c-initialize-cc-mode t)
-  (set-syntax-table c-mode-syntax-table)
-  (setq local-abbrev-table c-mode-abbrev-table
-	abbrev-mode t)
-  (use-local-map c-mode-map)
   (c-init-language-vars-for 'c-mode)
   (c-make-macro-with-semi-re) ; matches macro names whose expansion ends with ;
   (c-common-init 'c-mode)
@@ -1495,9 +1506,8 @@ Key bindings:
   "Abbreviation table used in c++-mode buffers.")
 
 (defvar c++-mode-map
-  (let ((map (c-make-inherited-keymap)))
+  (let ((map (make-sparse-keymap)))
     ;; Add bindings which are only useful for C++.
-    (define-key map "\C-c\C-e" 'c-macro-expand)
     (define-key map "\C-c:"    'c-scope-operator)
     (define-key map "<"        'c-electric-lt-gt)
     (define-key map ">"        'c-electric-lt-gt)
@@ -1508,7 +1518,7 @@ Key bindings:
 		  (cons "C++" (c-lang-const c-mode-menu c++)))
 
 ;;;###autoload
-(define-derived-mode c++-mode prog-mode "C++"
+(define-derived-mode c++-mode c-derivative-mode "C++"
   "Major mode for editing C++ code.
 To submit a problem report, enter `\\[c-submit-bug-report]' from a
 c++-mode buffer.  This automatically sets up a mail buffer with
@@ -1523,11 +1533,6 @@ initialization, then `c++-mode-hook'.
 
 Key bindings:
 \\{c++-mode-map}"
-  (c-initialize-cc-mode t)
-  (set-syntax-table c++-mode-syntax-table)
-  (setq local-abbrev-table c++-mode-abbrev-table
-	abbrev-mode t)
-  (use-local-map c++-mode-map)
   (c-init-language-vars-for 'c++-mode)
   (c-make-macro-with-semi-re) ; matches macro names whose expansion ends with ;
   (c-common-init 'c++-mode)
@@ -1549,9 +1554,8 @@ Key bindings:
   "Abbreviation table used in objc-mode buffers.")
 
 (defvar objc-mode-map
-  (let ((map (c-make-inherited-keymap)))
+  (let ((map (make-sparse-keymap)))
     ;; Add bindings which are only useful for Objective-C.
-    (define-key map "\C-c\C-e" 'c-macro-expand)
     map)
   "Keymap used in objc-mode buffers.")
 
@@ -1561,7 +1565,7 @@ Key bindings:
 ;;;###autoload (add-to-list 'auto-mode-alist '("\\.m\\'" . objc-mode))
 
 ;;;###autoload
-(define-derived-mode objc-mode prog-mode "ObjC"
+(define-derived-mode objc-mode c-derivative-mode "ObjC"
   "Major mode for editing Objective C code.
 To submit a problem report, enter `\\[c-submit-bug-report]' from an
 objc-mode buffer.  This automatically sets up a mail buffer with
@@ -1576,11 +1580,6 @@ initialization, then `objc-mode-hook'.
 
 Key bindings:
 \\{objc-mode-map}"
-  (c-initialize-cc-mode t)
-  (set-syntax-table objc-mode-syntax-table)
-  (setq local-abbrev-table objc-mode-abbrev-table
-	abbrev-mode t)
-  (use-local-map objc-mode-map)
   (c-init-language-vars-for 'objc-mode)
   (c-make-macro-with-semi-re) ; matches macro names whose expansion ends with ;
   (c-common-init 'objc-mode)
@@ -1604,7 +1603,7 @@ Key bindings:
   "Abbreviation table used in java-mode buffers.")
 
 (defvar java-mode-map
-  (let ((map (c-make-inherited-keymap)))
+  (let ((map (make-sparse-keymap)))
     ;; Add bindings which are only useful for Java.
     map)
   "Keymap used in java-mode buffers.")
@@ -1622,7 +1621,7 @@ Key bindings:
 ;;;###autoload (add-to-list 'auto-mode-alist '("\\.java\\'" . java-mode))
 
 ;;;###autoload
-(define-derived-mode java-mode prog-mode "Java"
+(define-derived-mode java-mode c-mode-common "Java"
   "Major mode for editing Java code.
 To submit a problem report, enter `\\[c-submit-bug-report]' from a
 java-mode buffer.  This automatically sets up a mail buffer with
@@ -1637,11 +1636,6 @@ initialization, then `java-mode-hook'.
 
 Key bindings:
 \\{java-mode-map}"
-  (c-initialize-cc-mode t)
-  (set-syntax-table java-mode-syntax-table)
-  (setq local-abbrev-table java-mode-abbrev-table
-	abbrev-mode t)
-  (use-local-map java-mode-map)
   (c-init-language-vars-for 'java-mode)
   (c-common-init 'java-mode)
   (easy-menu-add c-java-menu)
@@ -1660,7 +1654,7 @@ Key bindings:
   "Abbreviation table used in idl-mode buffers.")
 
 (defvar idl-mode-map
-  (let ((map (c-make-inherited-keymap)))
+  (let ((map (make-sparse-keymap)))
     ;; Add bindings which are only useful for IDL.
     map)
   "Keymap used in idl-mode buffers.")
@@ -1671,7 +1665,7 @@ Key bindings:
 ;;;###autoload (add-to-list 'auto-mode-alist '("\\.idl\\'" . idl-mode))
 
 ;;;###autoload
-(define-derived-mode idl-mode prog-mode "IDL"
+(define-derived-mode idl-mode c-mode-common "IDL"
   "Major mode for editing CORBA's IDL, PSDL and CIDL code.
 To submit a problem report, enter `\\[c-submit-bug-report]' from an
 idl-mode buffer.  This automatically sets up a mail buffer with
@@ -1686,10 +1680,7 @@ initialization, then `idl-mode-hook'.
 
 Key bindings:
 \\{idl-mode-map}"
-  (c-initialize-cc-mode t)
-  (set-syntax-table idl-mode-syntax-table)
-  (setq local-abbrev-table idl-mode-abbrev-table)
-  (use-local-map idl-mode-map)
+  (kill-local-variable 'abbrev-mode) ;FIXME: Only mode that doesn't enable it!?
   (c-init-language-vars-for 'idl-mode)
   (c-common-init 'idl-mode)
   (easy-menu-add c-idl-menu)
@@ -1710,7 +1701,7 @@ Key bindings:
   "Abbreviation table used in pike-mode buffers.")
 
 (defvar pike-mode-map
-  (let ((map (c-make-inherited-keymap)))
+  (let ((map (make-sparse-keymap)))
     ;; Additional bindings.
     (define-key map "\C-c\C-e" 'c-macro-expand)
     map)
@@ -1723,7 +1714,7 @@ Key bindings:
 ;;;###autoload (add-to-list 'interpreter-mode-alist '("pike" . pike-mode))
 
 ;;;###autoload
-(define-derived-mode pike-mode prog-mode "Pike"
+(define-derived-mode pike-mode c-mode-common "Pike"
   "Major mode for editing Pike code.
 To submit a problem report, enter `\\[c-submit-bug-report]' from a
 pike-mode buffer.  This automatically sets up a mail buffer with
@@ -1738,11 +1729,6 @@ initialization, then `pike-mode-hook'.
 
 Key bindings:
 \\{pike-mode-map}"
-  (c-initialize-cc-mode t)
-  (set-syntax-table pike-mode-syntax-table)
-  (setq local-abbrev-table pike-mode-abbrev-table
-	abbrev-mode t)
-  (use-local-map pike-mode-map)
   (c-init-language-vars-for 'pike-mode)
   (c-common-init 'pike-mode)
   (easy-menu-add c-pike-menu)
@@ -1765,11 +1751,11 @@ Key bindings:
   "Abbreviation table used in awk-mode buffers.")
 
 (defvar awk-mode-map
-  (let ((map (c-make-inherited-keymap)))
+  (let ((map (make-sparse-keymap)))
     ;; Add bindings which are only useful for awk.
-    (define-key map "#" 'self-insert-command)
-    (define-key map "/" 'self-insert-command)
-    (define-key map "*" 'self-insert-command)
+    (define-key map "#" 'self-insert-command);Override electric parent binding.
+    (define-key map "/" 'self-insert-command);Override electric parent binding.
+    (define-key map "*" 'self-insert-command);Override electric parent binding.
     (define-key map "\C-c\C-n" 'undefined) ; #if doesn't exist in awk.
     (define-key map "\C-c\C-p" 'undefined)
     (define-key map "\C-c\C-u" 'undefined)
@@ -1788,7 +1774,7 @@ Key bindings:
 (declare-function c-awk-unstick-NL-prop "cc-awk" ())
 
 ;;;###autoload
-(define-derived-mode awk-mode prog-mode "AWK"
+(define-derived-mode awk-mode c-mode-common "AWK"
   "Major mode for editing AWK code.
 To submit a problem report, enter `\\[c-submit-bug-report]' from an
 awk-mode buffer.  This automatically sets up a mail buffer with version
@@ -1807,11 +1793,6 @@ Key bindings:
   ;; declared in cc-awk.el and hasn't yet been loaded.
   :syntax-table nil
   (require 'cc-awk)			; Added 2003/6/10.
-  (c-initialize-cc-mode t)
-  (set-syntax-table awk-mode-syntax-table)
-  (setq local-abbrev-table awk-mode-abbrev-table
-	abbrev-mode t)
-  (use-local-map awk-mode-map)
   (c-init-language-vars-for 'awk-mode)
   (c-common-init 'awk-mode)
   (c-awk-unstick-NL-prop)