From: Alan Mackenzie Date: Sun, 8 Mar 2020 16:21:15 +0000 (+0000) Subject: CC Mode: allow specified directives (e.g. pragma) to be indented as statements X-Git-Tag: emacs-28.0.90~7784 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=35a13fca32c3371ca25d87f7447b4bd4f65de710;p=emacs.git CC Mode: allow specified directives (e.g. pragma) to be indented as statements * 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. --- diff --git a/doc/misc/cc-mode.texi b/doc/misc/cc-mode.texi index 544ff853351..f99a890670f 100644 --- a/doc/misc/cc-mode.texi +++ b/doc/misc/cc-mode.texi @@ -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 diff --git a/lisp/progmodes/cc-cmds.el b/lisp/progmodes/cc-cmds.el index a60812230b8..1b557c41a5d 100644 --- a/lisp/progmodes/cc-cmds.el +++ b/lisp/progmodes/cc-cmds.el @@ -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) ;; 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"))) + (declare-function subword-forward "subword" (&optional arg)) diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el index e7e7cfd4b09..1e72352f719 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el @@ -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")) - +(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. diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el index a39c50e4138..f92d3efdeb7 100644 --- a/lisp/progmodes/cc-mode.el +++ b/lisp/progmodes/cc-mode.el @@ -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- and C-c C- ;; 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) diff --git a/lisp/progmodes/cc-vars.el b/lisp/progmodes/cc-vars.el index 556ff6059f1..3995b211854 100644 --- a/lisp/progmodes/cc-vars.el +++ b/lisp/progmodes/cc-vars.el @@ -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)