Re: cc-mode gets confused by operator""
Alan Mackenzie <[email protected]> Tue, 6 Sep 2022 13:28:29 +0000
| Newsgroups | gmane.emacs.cc-mode.general |
|---|---|
| Message-ID | <YxdK/cUUb619h8CA@ACM> |
Hello! Firstly, sorry it's taken me so long to reply to your bug report. I put it to one side to deal with "later", and then forgot about it. :-( On Sun, Jun 26, 2022 at 14:34:03 -0700, [email protected] wrote: > cc-mode doesn't seem to be able to handle user-defined literals. I've > attached a simple test case. When editing this file with emacs 28.1 and > cc-mode 5.35.1, you will see the following: Thanks for taking the trouble to report this bug, and thanks even more for making it concise and easy to follow and reproduce. > As you can see from the font color, basically the whole rest of the file > starting the line after the operator"" gets treated as a raw multi-line > string. What was happening is that the C++ Mode initialisation was case-folding its search for R"....( and so parsed the final r in operator"" as an R. So it fontified the rest of the file as a raw string, since it couldn't find the "matching" closing delimiter, )"_hexstring". The fix is simply binding the Emacs Lisp variable case-fold-search to nil in the Emacs Lisp function c-common-init in the file emacs/lisp/progmodes/cc-mode.el. Can I ask you please to apply the patch below to your copy of Emacs 28.1, then byte compile the result by doing something like: path/to/emacs/lisp/progmodes $ emacs -Q -batch -f batch-byte-compile cc-mode.el .. The patch is basically just two lines, but there's a lot of code needing re-indentation, too. Please then try out the new CC Mode with the problematic C++ source code, and confirm that it has actually fixed the problem (or tell me what's still wrong). If you want any help with the patching, or byte compiling, please feel free to send me private email. The fix will be in Emacs 28.2 and later, when they get released. > // g++ -std=c++20 hexstring.cc > #include <iostream> > #include <string> > constexpr int > hexdigit(char c) > { > return (c >= '0' && c <= '9') ? c - '0' > : (c >= 'a' && c <= 'f') ? c - ('a' - 10) > : (c >= 'A' && c <= 'F') ? c - ('A' - 10) > : -1; > } > template<char ...C> > requires (sizeof...(C)%2 == 0 && > [](char zero, char x, auto ...rest) { > return zero == '0' && x == 'x' && ((hexdigit(rest) != -1) && ...); > }(C...)) > constexpr std::string > operator""_hexstring() > { > constexpr char digits[] = { C... }; > std::string result{}; > for (std::size_t i = 2; i < sizeof(digits); i += 2) > result += char(hexdigit(digits[i])<<4 | hexdigit(digits[i+1])); > return result; > } > int > main() > { > std::cout << 0x400a_hexstring; > } Here's the patch: --- lisp/progmodes/cc-mode.el~ 2022-03-11 07:04:21.000000000 +0000 +++ lisp/progmodes/cc-mode.el 2022-09-06 13:01:49.518458857 +0000 @@ -787,43 +787,44 @@ `c-basic-common-init' for details. It's only optional to be compatible with old code; callers should always specify it." - (unless mode - ;; Called from an old third party package. The fallback is to - ;; initialize for C. - (c-init-language-vars-for 'c-mode)) + (let (case-fold-search) + (unless mode + ;; Called from an old third party package. The fallback is to + ;; initialize for C. + (c-init-language-vars-for 'c-mode)) - (c-basic-common-init mode c-default-style) - (when mode - ;; Only initialize font locking if we aren't called from an old package. - (c-font-lock-init)) + (c-basic-common-init mode c-default-style) + (when mode + ;; Only initialize font locking if we aren't called from an old package. + (c-font-lock-init)) - ;; Starting a mode is a sort of "change". So call the change functions... - (save-restriction - (widen) - (setq c-new-BEG (point-min)) - (setq c-new-END (point-max)) - (save-excursion - (let (before-change-functions after-change-functions) - (mapc (lambda (fn) - (funcall fn (point-min) (point-max))) - c-get-state-before-change-functions) - (mapc (lambda (fn) - (funcall fn (point-min) (point-max) - (- (point-max) (point-min)))) - c-before-font-lock-functions)))) + ;; Starting a mode is a sort of "change". So call the change functions... + (save-restriction + (widen) + (setq c-new-BEG (point-min)) + (setq c-new-END (point-max)) + (save-excursion + (let (before-change-functions after-change-functions) + (mapc (lambda (fn) + (funcall fn (point-min) (point-max))) + c-get-state-before-change-functions) + (mapc (lambda (fn) + (funcall fn (point-min) (point-max) + (- (point-max) (point-min)))) + c-before-font-lock-functions)))) - (set (make-local-variable 'outline-regexp) "[^#\n\^M]") - (set (make-local-variable 'outline-level) 'c-outline-level) - (set (make-local-variable 'add-log-current-defun-function) - (lambda () - (or (c-cpp-define-name) (car (c-defun-name-and-limits nil))))) - (let ((rfn (assq mode c-require-final-newline))) - (when rfn - (if (boundp 'mode-require-final-newline) - (and (cdr rfn) - (set (make-local-variable 'require-final-newline) - mode-require-final-newline)) - (set (make-local-variable 'require-final-newline) (cdr rfn)))))) + (set (make-local-variable 'outline-regexp) "[^#\n\^M]") + (set (make-local-variable 'outline-level) 'c-outline-level) + (set (make-local-variable 'add-log-current-defun-function) + (lambda () + (or (c-cpp-define-name) (car (c-defun-name-and-limits nil))))) + (let ((rfn (assq mode c-require-final-newline))) + (when rfn + (if (boundp 'mode-require-final-newline) + (and (cdr rfn) + (set (make-local-variable 'require-final-newline) + mode-require-final-newline)) + (set (make-local-variable 'require-final-newline) (cdr rfn))))))) (defun c-count-cfss (lv-alist) ;; LV-ALIST is an alist like `file-local-variables-alist'. Count how many -- Alan Mackenzie (Nuremberg, Germany).