1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-02-08 00:33:13 -08:00

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.
This commit is contained in:
Alan Mackenzie 2020-03-08 16:21:15 +00:00
parent 66bc47d12a
commit 35a13fca32
5 changed files with 185 additions and 39 deletions

View 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

View 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)
;; 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))

View 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"))
(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.

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

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