]> git.eshelyaron.com Git - emacs.git/commitdiff
CC Mode: allow specified directives (e.g. pragma) to be indented as statements
authorAlan Mackenzie <acm@muc.de>
Sun, 8 Mar 2020 16:21:15 +0000 (16:21 +0000)
committerAlan Mackenzie <acm@muc.de>
Sun, 8 Mar 2020 16:21:15 +0000 (16:21 +0000)
* lisp/progmodes/cc-cmds.el (c-align-cpp-indent-to-body)
(c-cpp-indent-to-body-flag, c-electric-pragma)
(c-add-indent-to-body-to-abbrev-table, c-clear-stale-indent-to-body-abbrevs)
(c-toggle-cpp-indent-to-body): New functions and variables.

* lisp/progmodes/cc-langs.el (c-std-abbrev-keywords): New lang const/var.

* lisp/progmodes/cc-mode.el (c-populate-abbrev-table): New function.
(c-basic-common-init): call the c-populate-abbrev-table.
(c-mode, c++-mode, objc-mode, java-mode, idl-mode, pike-mode, awk-mode):
Remove the setting of MODE-abbrev-table.

* lisp/progmodes/cc-vars.el (c-cpp-indent-to-body-directives): New defcustom.

* doc/misc/cc-mode.texi (Custom Macros): Introduce and refer to ....
(Indenting Directives): New page documenting the new mechanism.

doc/misc/cc-mode.texi
lisp/progmodes/cc-cmds.el
lisp/progmodes/cc-langs.el
lisp/progmodes/cc-mode.el
lisp/progmodes/cc-vars.el

index 544ff85335174ca778cffd0176ab8c1cce6044c3..f99a890670fbc6540fa031718bdd8dab69efba10 100644 (file)
@@ -350,11 +350,12 @@ Line-Up Functions
 * Misc Line-Up::
 
 
-Customizing Macros
+Custom Macros
 
 * Macro Backslashes::
 * Macros with ;::
 * Noise Macros::
+* Indenting Directives::
 
 @end detailmenu
 @end menu
@@ -6949,6 +6950,10 @@ is @code{nil}, all lines inside macro definitions are analyzed as
 @code{cpp-macro-cont}.
 @end defopt
 
+Sometimes you may want to indent particular directives
+(e.g. @code{#pragma}) as though they were statements.  To do this, see
+@ref{Indenting Directives}.
+
 Because a macro can expand into anything at all, near where one is
 invoked @ccmode{} can only indent and fontify code heuristically.
 Sometimes it gets it wrong.  Usually you should try to design your
@@ -6965,6 +6970,7 @@ Macros}.
 * Macro Backslashes::
 * Macros with ;::
 * Noise Macros::
+* Indenting Directives::
 @end menu
 
 @comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@@ -7074,7 +7080,7 @@ initialization code, after the mode hooks have run.
 @end defun
 
 @comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-@node    Noise Macros,  , Macros with ;, Custom Macros
+@node    Noise Macros, Indenting Directives, Macros with ;, Custom Macros
 @comment node-name, next, previous, up
 @section Noise Macros
 @cindex noise macros
@@ -7130,6 +7136,48 @@ has run.  This function is called by @ccmode{}'s initialization code,
 after the mode hooks have run.
 @end defun
 
+@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+@node    Indenting Directives, , Noise Macros, Custom Macros
+@comment node-name, next, previous, up
+@section Indenting Directives
+@cindex Indenting Directives
+@cindex Indenting #pragma
+@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+Sometimes you may want to indent particular preprocessor directives
+(e.g. @code{#pragma}) as though they were statements.  To do this,
+first set up @code{c-cpp-indent-to-body-directives} to include the
+directive name(s), then enable the ``indent to body'' feature with
+@code{c-toggle-cpp-indent-to-body}.
+
+@defopt c-cpp-indent-to-body-directives
+@vindex cpp-indent-to-body-directives (c-)
+This variable is a list of names of CPP directives (not including the
+introducing @samp{#}) which will be indented as though statements.
+Each element is a string, and must be a valid identifier.  The default
+value is @code{("pragma")}.
+
+If you add more directives to this variable, or remove directives from
+it, whilst ``indent to body'' is active, you need to re-enable the
+feature by calling @code{c-toggle-cpp-indent-to-body} for these
+changes to take effect@footnote{Note that the removal of directives
+doesn't work satisfactorally on XEmacs or on very old versions of
+Emacs}.
+@end defopt
+
+@defun c-toggle-cpp-indent-to-body
+@findex toggle-cpp-indent-to-body (c-)
+With @kbd{M-x c-toggle-cpp-indent-to-body}, you enable or disable the
+``indent to body'' feature.  When called programmatically, it takes an
+optional numerical argument.  A positive value will enable the
+feature, a zero or negative value will disable it.
+
+You should set up @code{c-cpp-indent-to-body-directives} before
+calling this function, since the function sets internal state which
+depends on that variable.
+@end defun
+
+
 @comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 @node    Odds and Ends, Sample Init File, Custom Macros, Top
 @comment node-name, next, previous, up
index a60812230b8c95782763e67a3c15247c431d7704..1b557c41a5d4e746f87fa9eb8b4fcdefe577a450 100644 (file)
@@ -48,6 +48,7 @@
 (cc-bytecomp-defvar filladapt-mode)    ; c-fill-paragraph contains a kludge
                                        ; which looks at this.
 (cc-bytecomp-defun electric-pair-post-self-insert-function)
+(cc-bytecomp-defvar c-indent-to-body-directives)
 \f
 ;; Indentation / Display syntax functions
 (defvar c-fix-backslashes t)
@@ -1441,6 +1442,98 @@ keyword on the line, the keyword is not inserted inside a literal, and
          (indent-according-to-mode)
        (delete-char -2)))))
 
+(defun c-align-cpp-indent-to-body ()
+  "Align a \"#pragma\" line under the previous line.
+This function is intented for use as a member of `c-special-indent-hook'."
+  (when (assq 'cpp-macro c-syntactic-context)
+    (when
+       (save-excursion
+         (save-match-data
+           (back-to-indentation)
+           (and
+            (looking-at (concat c-opt-cpp-symbol "[ \t]*\\([a-zA-Z0-9_]+\\)"))
+            (member (match-string-no-properties 1)
+                    c-cpp-indent-to-body-directives))))
+      (c-indent-line (delete '(cpp-macro) c-syntactic-context)))))
+
+(defvar c-cpp-indent-to-body-flag nil)
+;; Non-nil when CPP directives such as "#pragma" should be indented to under
+;; the preceding statement.
+(make-variable-buffer-local 'c-cpp-indent-to-body-flag)
+
+(defun c-electric-pragma ()
+  "Reindent the current line if appropriate.
+
+This function is used to reindent a preprocessor line when the
+symbol for the directive, typically \"pragma\", triggers this
+function as a hook function of an abbreviation.
+
+The \"#\" of the preprocessor construct is aligned under the
+first anchor point of the line's syntactic context.
+
+The line is reindented if the construct is not in a string or
+comment, there is exactly one \"#\" contained in optional
+whitespace before it on the current line, and `c-electric-flag'
+and `c-syntactic-indentation' are both non-nil."
+  (save-excursion
+    (save-match-data
+      (when
+         (and
+          c-cpp-indent-to-body-flag
+          c-electric-flag
+          c-syntactic-indentation
+          last-abbrev-location
+          c-opt-cpp-symbol             ; "#" or nil.
+          (progn (back-to-indentation)
+                 (looking-at (concat c-opt-cpp-symbol "[ \t]*")))
+          (>= (match-end 0) last-abbrev-location)
+          (not (c-literal-limits)))
+       (c-indent-line (delete '(cpp-macro) (c-guess-basic-syntax)))))))
+
+(defun c-add-indent-to-body-to-abbrev-table (d)
+  ;; Create an abbreviation table entry for the directive D, and add it to the
+  ;; current abbreviation table.  Existing abbreviation (e.g. for "else") do
+  ;; not get overwritten.
+  (when (and c-buffer-is-cc-mode
+            local-abbrev-table
+            (not (abbrev-symbol d local-abbrev-table)))
+    (condition-case nil
+       (define-abbrev local-abbrev-table d d 'c-electric-pragma 0 t)
+      (wrong-number-of-arguments
+       (define-abbrev local-abbrev-table d d 'c-electric-pragma)))))
+
+(defun c-clear-stale-indent-to-body-abbrevs ()
+  ;; Fill in this comment.  FIXME!!!
+  (when (fboundp 'abbrev-get)
+    (mapatoms (lambda (a)
+               (when (and (abbrev-get a ':system) ; Preserve a user's abbrev!
+                          (not (member (symbol-name a) c-std-abbrev-keywords))
+                          (not (member (symbol-name a)
+                                       c-cpp-indent-to-body-directives)))
+                 (unintern a local-abbrev-table)))
+             local-abbrev-table)))
+
+(defun c-toggle-cpp-indent-to-body (&optional arg)
+  "Toggle the C preprocessor indent-to-body feature.
+When enabled, preprocessor directives which are words in
+`c-indent-to-body-directives' are indented as if they were statements.
+
+Optional numeric ARG, if supplied, turns on the feature when positive,
+turns it off when negative, and just toggles it when zero or
+left out."
+  (interactive "P")
+  (setq c-cpp-indent-to-body-flag
+       (c-calculate-state arg c-cpp-indent-to-body-flag))
+  (if c-cpp-indent-to-body-flag
+      (progn
+       (c-clear-stale-indent-to-body-abbrevs)
+       (mapc 'c-add-indent-to-body-to-abbrev-table
+             c-cpp-indent-to-body-directives)
+       (add-hook 'c-special-indent-hook 'c-align-cpp-indent-to-body nil t))
+    (remove-hook 'c-special-indent-hook 'c-align-cpp-indent-to-body t))
+  (message "c-cpp-indent-to-body %sabled"
+          (if c-cpp-indent-to-body-flag "en" "dis")))
+
 \f
 
 (declare-function subword-forward "subword" (&optional arg))
index e7e7cfd4b09f8e3d092f6013952bda00cb5baed7..1e72352f719d4c1b6c26257ed55b5c78162985fa 100644 (file)
@@ -3030,7 +3030,14 @@ Note that Java specific rules are currently applied to tell this from
         ;; can start a declaration.)
         "entity" "process" "service" "session" "storage"))
 
-\f
+(c-lang-defconst c-std-abbrev-keywords
+  "List of keywords which may need to cause electric indentation."
+  t '("else" "while")
+  c++ (append (c-lang-const c-std-abbrev-keywords) '("catch"))
+  java (append (c-lang-const c-std-abbrev-keywords) '("catch" "finally"))
+  idl nil)
+(c-lang-defvar c-std-abbrev-keywords (c-lang-const c-std-abbrev-keywords))
+
 ;;; Constants built from keywords.
 
 ;; Note: No `*-kwds' language constants may be defined below this point.
index a39c50e4138c086d2f08e679de72c056ddda6cd1..f92d3efdeb71d5d5ec5863a8806c280c06820b95 100644 (file)
@@ -278,6 +278,29 @@ control).  See \"cc-mode.el\" for more info."
       (setq defs (cdr defs)))))
 (put 'c-define-abbrev-table 'lisp-indent-function 1)
 
+(defun c-populate-abbrev-table ()
+  ;; Insert the standard keywords which may need electric indentation into the
+  ;; current mode's abbreviation table.
+  (let ((table (intern (concat (symbol-name major-mode) "-abbrev-table")))
+       (defs c-std-abbrev-keywords)
+       )
+    (unless (and (boundp table)
+                (abbrev-table-p (symbol-value table)))
+      (define-abbrev-table table nil))
+    (setq local-abbrev-table (symbol-value table))
+    (while defs
+      (unless (intern-soft (car defs) local-abbrev-table) ; Don't overwrite the
+                                       ; abbrev's use count.
+       (condition-case nil
+           (define-abbrev (symbol-value table)
+             (car defs) (car defs)
+             'c-electric-continued-statement 0 t)
+         (wrong-number-of-arguments
+          (define-abbrev (symbol-value table)
+            (car defs) (car defs)
+            'c-electric-continued-statement 0))))
+      (setq defs (cdr defs)))))
+
 (defun c-bind-special-erase-keys ()
   ;; Only used in Emacs to bind C-c C-<delete> and C-c C-<backspace>
   ;; to the proper keys depending on `normal-erase-is-backspace'.
@@ -550,6 +573,8 @@ that requires a literal mode spec at compile time."
 
   (setq c-buffer-is-cc-mode mode)
 
+  (c-populate-abbrev-table)
+
   ;; these variables should always be buffer local; they do not affect
   ;; indentation style.
   (make-local-variable 'comment-start)
@@ -2444,11 +2469,6 @@ opening \" and the next unescaped end of line."
   (funcall (c-lang-const c-make-mode-syntax-table c))
   "Syntax table used in c-mode buffers.")
 
-(c-define-abbrev-table 'c-mode-abbrev-table
-  '(("else" "else" c-electric-continued-statement 0)
-    ("while" "while" c-electric-continued-statement 0))
-  "Abbreviation table used in c-mode buffers.")
-
 (defvar c-mode-map
   (let ((map (c-make-inherited-keymap)))
     map)
@@ -2560,12 +2580,6 @@ the code is C or C++ and based on that chooses whether to enable
   (funcall (c-lang-const c-make-mode-syntax-table c++))
   "Syntax table used in c++-mode buffers.")
 
-(c-define-abbrev-table 'c++-mode-abbrev-table
-  '(("else" "else" c-electric-continued-statement 0)
-    ("while" "while" c-electric-continued-statement 0)
-    ("catch" "catch" c-electric-continued-statement 0))
-  "Abbreviation table used in c++-mode buffers.")
-
 (defvar c++-mode-map
   (let ((map (c-make-inherited-keymap)))
     map)
@@ -2614,11 +2628,6 @@ Key bindings:
   (funcall (c-lang-const c-make-mode-syntax-table objc))
   "Syntax table used in objc-mode buffers.")
 
-(c-define-abbrev-table 'objc-mode-abbrev-table
-  '(("else" "else" c-electric-continued-statement 0)
-    ("while" "while" c-electric-continued-statement 0))
-  "Abbreviation table used in objc-mode buffers.")
-
 (defvar objc-mode-map
   (let ((map (c-make-inherited-keymap)))
     map)
@@ -2665,13 +2674,6 @@ Key bindings:
   (funcall (c-lang-const c-make-mode-syntax-table java))
   "Syntax table used in java-mode buffers.")
 
-(c-define-abbrev-table 'java-mode-abbrev-table
-  '(("else" "else" c-electric-continued-statement 0)
-    ("while" "while" c-electric-continued-statement 0)
-    ("catch" "catch" c-electric-continued-statement 0)
-    ("finally" "finally" c-electric-continued-statement 0))
-  "Abbreviation table used in java-mode buffers.")
-
 (defvar java-mode-map
   (let ((map (c-make-inherited-keymap)))
     map)
@@ -2722,9 +2724,6 @@ Key bindings:
   (funcall (c-lang-const c-make-mode-syntax-table idl))
   "Syntax table used in idl-mode buffers.")
 
-(c-define-abbrev-table 'idl-mode-abbrev-table nil
-  "Abbreviation table used in idl-mode buffers.")
-
 (defvar idl-mode-map
   (let ((map (c-make-inherited-keymap)))
     map)
@@ -2767,11 +2766,6 @@ Key bindings:
   (funcall (c-lang-const c-make-mode-syntax-table pike))
   "Syntax table used in pike-mode buffers.")
 
-(c-define-abbrev-table 'pike-mode-abbrev-table
-  '(("else" "else" c-electric-continued-statement 0)
-    ("while" "while" c-electric-continued-statement 0))
-  "Abbreviation table used in pike-mode buffers.")
-
 (defvar pike-mode-map
   (let ((map (c-make-inherited-keymap)))
     map)
@@ -2819,11 +2813,6 @@ Key bindings:
 ;;;###autoload (add-to-list 'interpreter-mode-alist '("nawk" . awk-mode))
 ;;;###autoload (add-to-list 'interpreter-mode-alist '("gawk" . awk-mode))
 
-(c-define-abbrev-table 'awk-mode-abbrev-table
-  '(("else" "else" c-electric-continued-statement 0)
-    ("while" "while" c-electric-continued-statement 0))
-  "Abbreviation table used in awk-mode buffers.")
-
 (defvar awk-mode-map
   (let ((map (c-make-inherited-keymap)))
     map)
index 556ff6059f12dbe9519f79934a4be46073c54e93..3995b211854a9ae032e9859894560bde13c5e597 100644 (file)
@@ -1649,6 +1649,15 @@ white space either before or after the operator, but not both."
   :type 'boolean
   :group 'c)
 
+(defcustom c-cpp-indent-to-body-directives '("pragma")
+  "Preprocessor directives which will be indented as statements.
+
+A list of Preprocessor directives which when reindented, or newly
+typed in, will cause the \"#\" introducing the directive to be
+indented as a statement."
+  :type '(repeat string)
+  :group 'c)
+
 ;; Initialize the next two to a regexp which never matches.
 (defvar c-noise-macro-with-parens-name-re regexp-unmatchable)
 (make-variable-buffer-local 'c-noise-macro-with-parens-name-re)