[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: c-mode pragma and preproc

From: Ergus
Subject: Re: c-mode pragma and preproc
Date: Wed, 12 Feb 2020 11:19:56 +0100

Hi Alan:

Sorry for not commenting before the feedback.

Yes it works fine for me. Except for the details I mentioned before
about C-c C-o. But I understand the limitations we have there. So in my
opinion you can commit it.

Very thanks for this,

On Tue, Feb 11, 2020 at 08:00:18PM +0000, Alan Mackenzie wrote:
Hello again, Ergus.

Just a quick ping!

I've now written the requisite documentation for the CC Mode manual.
Are you otherwise happy (or, at least, happy enough) with the state of
the code for me to commit it?

Alan Mackenzie (Nuremberg, Germany).

On Mon, Jan 20, 2020 at 21:27:02 +0000, Alan Mackenzie wrote:
On Mon, Jan 20, 2020 at 03:15:38 +0100, Ergus via Emacs development 
discussions. wrote:
> Hi Alan:

> I just tried the patch and it seems to work as expected.


> I just have 3 observations/suggestions/questions:

> 1) When I stay in column 0 like
> |#pragma bla bla

> and I press tab (to indent the line's) I get:
> |    #pragma bla bla

> instead of:
>      |#pragma bla bla

> as it happens with other lines (where | is the cursor position). Is this
> intended?

Whoops!  That sort of thing is why I normally ask bug reporters to check
the patch before I commit it.  It's difficult to see when one is fixing a
bug, but stands out like a sore thumb to somebody trying it out.

What happened was I called c-indent-line from inside a save-excursion.
c-indent-line placed point correctly, but emerging from the
save-excursion put it back to column 0.  To fix this, either substitute
this new version of c-align-cpp-indent-to-body for the old one, or
reapply the patch below from scratch (whichever you prefer):

(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)
            (looking-at "#[ \t]*pragma\\([^[:alnum:]_$]\\|$\\)")))
      (c-indent-line (delete '(cpp-macro) c-syntactic-context)))))


> 2) This new implementation breaks the "normal" way to configure the
> indentation in c-mode because C-c C-o still returns cpp-macro (as
> expected) while the indentation is not behaving as specified for
> cpp-macros. So it seems to be confusing for the user and somehow a bit
> inconsistent with the rest of cc-mode indentation.

It does a bit, yes.  This is an inevitable downside of using
c-special-indent-hook which counterbalances to some extent the ability to
add lightweight, yet effective, solutions to otherwise awkward
indentation problems.  The other standard c-special-indent-hook function
is c-gnu-impose-minimum, which in gnu style ensures that source code
within functions has a minimum indentation.  That also suffers from the
inability to use C-c C-o with it.

> Why not just add an extra syntactic symbol for pragmas? Isn't it more
> consistent that way?

There's no "just" about a new syntactic symbol.  It would be more work to
back it, more likely to interact with other features, and more likely to
increase the complexity of CC Mode more than needed.  Consider that, in
reality, #pragma _is_ a cpp-macro, not something new.

> Otherwise the user will get false information from C-c C-o and looses
> somehow a bit of flexibility.

This is true.

> Maybe we just need a variable that when not specified indent pragmas as
> macros else indent pragmas as it specifies...?

Are you thinking of c-cpp-indent-to-body-flag?  ;-)

> 3) Why the new function is called c-toggle-cpp-indent*? pragmas are part
> of C too. Actually in HPC we usually use more C (and Fortran) than C++.

cpp for "C PreProcessor".  :-)  I don't think there are any instances of
cpp meaning C++ in CC Mode, but I agree it's an unfortunate collision of
abbreviations.  :-(

I still haven't amended the CC Mode manual, but I will.

> In any case the only real "issue" in my opinion is 1). But so far it
> works pretty fine.

OK, here's the replacement patch:

diff --git a/lisp/progmodes/cc-cmds.el b/lisp/progmodes/cc-cmds.el
index 1071191775..56e3421ba2 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,79 @@ c-electric-continued-statement
        (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)
+           (looking-at "#[ \t]*pragma\\([^[:alnum:]_$]\\|$\\)")))
+      (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)))
+    (define-abbrev local-abbrev-table d d 'c-electric-pragma)))
+(defun c-toggle-cpp-indent-to-body (&optional arg)
+  "Toggle the cpp indent-to-body feature.
+When enabled, when CPP directives which are words in
+`c-indent-to-body-directives', the are indented such that the
+initial \"#\" appears below the previous statement.
+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
+       (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)))

 (declare-function subword-forward "subword" (&optional arg))
diff --git a/lisp/progmodes/cc-vars.el b/lisp/progmodes/cc-vars.el
index 861872486c..0391cdccfd 100644
--- a/lisp/progmodes/cc-vars.el
+++ b/lisp/progmodes/cc-vars.el
@@ -1649,6 +1649,15 @@ c-asymmetry-fontification-flag
   :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)

> Very thanks,
> Ergus.

Alan Mackenzie (Nuremberg, Germany).

reply via email to

[Prev in Thread] Current Thread [Next in Thread]