From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Alan Mackenzie Newsgroups: gmane.emacs.bugs Subject: bug#33794: 26.1; electric-pair-mode breaks auto-newline minor mode of cc-mode Date: Tue, 1 Jan 2019 19:27:29 +0000 Message-ID: <20190101192729.GA22315@ACM> References: <20181221134829.29135.qmail@mail.muc.de> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit X-Trace: blaine.gmane.org 1546371313 20157 195.159.176.226 (1 Jan 2019 19:35:13 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Tue, 1 Jan 2019 19:35:13 +0000 (UTC) User-Agent: Mutt/1.10.1 (2018-07-13) Cc: 33794@debbugs.gnu.org To: bea@klebe.blog Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Tue Jan 01 20:35:09 2019 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1gePoh-000571-7V for geb-bug-gnu-emacs@m.gmane.org; Tue, 01 Jan 2019 20:35:07 +0100 Original-Received: from localhost ([127.0.0.1]:39542 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gePqn-00006U-SI for geb-bug-gnu-emacs@m.gmane.org; Tue, 01 Jan 2019 14:37:17 -0500 Original-Received: from eggs.gnu.org ([208.118.235.92]:46279) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gePqe-000069-1C for bug-gnu-emacs@gnu.org; Tue, 01 Jan 2019 14:37:09 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gePqZ-00013g-Q5 for bug-gnu-emacs@gnu.org; Tue, 01 Jan 2019 14:37:07 -0500 Original-Received: from debbugsout.gnu.org ([209.51.188.43]:33714) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1gePqY-00012d-3s; Tue, 01 Jan 2019 14:37:02 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1gePqX-0003cM-O3; Tue, 01 Jan 2019 14:37:01 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Alan Mackenzie Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org, bug-cc-mode@gnu.org Resent-Date: Tue, 01 Jan 2019 19:37:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 33794 X-GNU-PR-Package: emacs,cc-mode X-GNU-PR-Keywords: Original-Received: via spool by 33794-submit@debbugs.gnu.org id=B33794.154637137213843 (code B ref 33794); Tue, 01 Jan 2019 19:37:01 +0000 Original-Received: (at 33794) by debbugs.gnu.org; 1 Jan 2019 19:36:12 +0000 Original-Received: from localhost ([127.0.0.1]:44152 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1gePpj-0003bC-9n for submit@debbugs.gnu.org; Tue, 01 Jan 2019 14:36:11 -0500 Original-Received: from colin.muc.de ([193.149.48.1]:40517 helo=mail.muc.de) by debbugs.gnu.org with smtp (Exim 4.84_2) (envelope-from ) id 1gePpg-0003b3-Cp for 33794@debbugs.gnu.org; Tue, 01 Jan 2019 14:36:09 -0500 Original-Received: (qmail 95688 invoked by uid 3782); 1 Jan 2019 19:36:06 -0000 Original-Received: from acm.muc.de (p2E5D5A97.dip0.t-ipconnect.de [46.93.90.151]) by colin.muc.de (tmda-ofmipd) with ESMTP; Tue, 01 Jan 2019 20:36:06 +0100 Original-Received: (qmail 22396 invoked by uid 1000); 1 Jan 2019 19:27:29 -0000 Content-Disposition: inline In-Reply-To: X-Delivery-Agent: TMDA/1.1.12 (Macallan) X-Primary-Address: acm@muc.de X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.51.188.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.org gmane.emacs.bugs:154071 Archived-At: Hello, Beatrix, Happy New Year! On Fri, Dec 21, 2018 at 11:00:10 -0500, Beatrix Klebe wrote: > What would be ideal, and what I'm looking for, is to get auto-pairing > of brackets with braces being placed where they should be > automatically and the insertion point getting put in between them at > the correct indent level, such as what happens with Visual Studio, or > Visual Studio Code, or several other editors with this functionality. > Perhaps it is not emacslike to have such behavior be totally > automated, but I am used to it and finds it decreases my ordinary > levels of frustration when working with verbose and imperative > languages. I am currently trying to write some insert specifiers for > smartparens to do this, but it is proving more difficult to find an > elegant solution than I had expected. I think the following patch to CC Mode gives you nearly everything you want, if not actually everything. It turned out that the amendment didn't require any modification to electric-pair-mode, so apologies to Joćo. I don't know how much you've explored electric-pair-mode, but if the answer is "not very much", can I suggest you try setting electric-pair-skip-whitespace to 'chomp? The following editing pattern is then available. With electric-pair-mode and c-auto-newline mode enabled: ("|" represents point.) At the end of the line if (foo)| foo = bar; , type {. This will give you something like: if (foo) { | } foo = bar; . Type in a statement ending with a semicolon: if (foo) { foo = bar; | } foo = bar; . Now type in }. The effect is to "chomp" the space to the next }, and CC Mode's auto-newline then inserts an empty line after the brace: if (foo) { foo = bar; } | foo = bar; . So, please try out the patch, and please let us all know how well it corresponds with what you were looking for. Also please let me know about any bugs you notice, so that I can fix them. Thanks for such an interesting problem! Here's the patch, which should apply cleanly to the emacs-26.1 source: diff --git a/lisp/progmodes/cc-cmds.el b/lisp/progmodes/cc-cmds.el index 65b44339bc..8038f29d3e 100644 --- a/lisp/progmodes/cc-cmds.el +++ b/lisp/progmodes/cc-cmds.el @@ -47,6 +47,7 @@ ;; Silence the compiler. (cc-bytecomp-defvar filladapt-mode) ; c-fill-paragraph contains a kludge ; which looks at this. +(cc-bytecomp-defun electric-pair-post-self-insert-function) ;; Indentation / Display syntax functions (defvar c-fix-backslashes t) @@ -503,7 +504,8 @@ c-electric-pound (eq (char-before) ?\\)))) (c-in-literal))) ;; do nothing special - (self-insert-command (prefix-numeric-value arg)) + (let (post-self-insert-hook) ; Disable random functionality. + (self-insert-command (prefix-numeric-value arg))) ;; place the pound character at the left edge (let ((pos (- (point-max) (point))) (bolp (bolp))) @@ -694,6 +696,134 @@ c-try-one-liner t)))) (goto-char (- (point-max) pos)))))) +(defun c-do-brace-electrics (before after) + ;; Point is just after a brace. Indent the various lines, add any required + ;; auto newlines, and apply pertinent clean ups. It is assumed that the + ;; caller has checked that point is at EOL if need be, and that the brace is + ;; not in a comment or string, and suchlike. + ;; + ;; BEFORE and AFTER qualify the newlines required before and after the + ;; brace as follows: + ;; If + ;; o - nil: insert a newline or not according to `c-hanging-braces-alist'. + ;; o - 'ignore: don't insert a newline. + ;; o - 'assume: insert a newline. + ;; + ;; The return value has no significance. + (let (;; shut this up too + (c-echo-syntactic-information-p nil) + newlines + ln-syntax br-syntax syntax) ; Syntactic context of the original line, + ; of the brace itself, of the line the + ; brace ends up on. + (c-save-buffer-state ((c-syntactic-indentation-in-macros t) + (c-auto-newline-analysis t)) + (setq ln-syntax (c-guess-basic-syntax))) + (if c-syntactic-indentation + (c-indent-line ln-syntax)) + + (when c-auto-newline + (backward-char) + (setq br-syntax (c-point-syntax) + newlines (c-brace-newlines br-syntax)) + + ;; Insert the BEFORE newline, if wanted, and reindent the newline. + (if (or (and (null before) (memq 'before newlines) + (> (current-column) (current-indentation))) + (eq before 'assume)) + (if c-syntactic-indentation + ;; Only a plain newline for now - it's indented + ;; after the cleanups when the line has its final + ;; appearance. + (newline) + (c-newline-and-indent))) + (forward-char) + + ;; `syntax' is the syntactic context of the line which ends up + ;; with the brace on it. + (setq syntax (if (memq 'before newlines) br-syntax ln-syntax)) + + ;; Do all appropriate clean ups + (let ((here (point)) + (pos (- (point-max) (point))) + mbeg mend + ) + + ;; `}': clean up empty defun braces + (when (c-save-buffer-state () + (and (memq 'empty-defun-braces c-cleanup-list) + (eq (c-last-command-char) ?\}) + (c-intersect-lists '(defun-close class-close inline-close) + syntax) + (progn + (forward-char -1) + (c-skip-ws-backward) + (eq (char-before) ?\{)) + ;; make sure matching open brace isn't in a comment + (not (c-in-literal)))) + (delete-region (point) (1- here)) + (setq here (- (point-max) pos))) + (goto-char here) + + ;; `}': compact to a one-liner defun? + (save-match-data + (when + (and (eq (c-last-command-char) ?\}) + (memq 'one-liner-defun c-cleanup-list) + (c-intersect-lists '(defun-close) syntax) + (c-try-one-liner)) + (setq here (- (point-max) pos)))) + + ;; `{': clean up brace-else-brace and brace-elseif-brace + (when (eq (c-last-command-char) ?\{) + (cond + ((and (memq 'brace-else-brace c-cleanup-list) + (re-search-backward + (concat "}" + "\\([ \t\n]\\|\\\\\n\\)*" + "else" + "\\([ \t\n]\\|\\\\\n\\)*" + "{" + "\\=") + nil t)) + (delete-region (match-beginning 0) (match-end 0)) + (insert-and-inherit "} else {")) + ((and (memq 'brace-elseif-brace c-cleanup-list) + (progn + (goto-char (1- here)) + (setq mend (point)) + (c-skip-ws-backward) + (setq mbeg (point)) + (eq (char-before) ?\))) + (zerop (c-save-buffer-state nil (c-backward-token-2 1 t))) + (eq (char-after) ?\() + (re-search-backward + (concat "}" + "\\([ \t\n]\\|\\\\\n\\)*" + "else" + "\\([ \t\n]\\|\\\\\n\\)+" + "if" + "\\([ \t\n]\\|\\\\\n\\)*" + "\\=") + nil t)) + (delete-region mbeg mend) + (goto-char mbeg) + (insert ?\ )))) + + (goto-char (- (point-max) pos)) + + ;; Indent the line after the cleanups since it might + ;; very well indent differently due to them, e.g. if + ;; c-indent-one-line-block is used together with the + ;; one-liner-defun cleanup. + (when c-syntactic-indentation + (c-indent-line))) + + ;; does a newline go after the brace? + (if (or (and (null after) (memq 'after newlines)) + (eq after 'assume)) + (c-newline-and-indent))))) + (defun c-electric-brace (arg) "Insert a brace. @@ -716,7 +846,10 @@ c-electric-brace ;; We want to inhibit blinking the paren since this would be ;; most disruptive. We'll blink it ourselves later on. (old-blink-paren blink-paren-function) - blink-paren-function case-fold-search) + blink-paren-function case-fold-search + (at-eol (looking-at "[ \t]*\\\\?$")) + (active-region (and (fboundp 'use-region-p) (use-region-p))) + got-pair-} electric-pair-deletion) (c-save-buffer-state () (setq safepos (c-safe-position (point) (c-parse-state)) @@ -724,128 +857,36 @@ c-electric-brace ;; Insert the brace. Note that expand-abbrev might reindent ;; the line here if there's a preceding "else" or something. - (self-insert-command (prefix-numeric-value arg)) - - (when (and c-electric-flag (not literal) (not arg)) - (if (not (looking-at "[ \t]*\\\\?$")) - (if c-syntactic-indentation - (indent-according-to-mode)) - - (let ( ;; shut this up too - (c-echo-syntactic-information-p nil) - newlines - ln-syntax br-syntax syntax) ; Syntactic context of the original line, - ; of the brace itself, of the line the brace ends up on. - (c-save-buffer-state ((c-syntactic-indentation-in-macros t) - (c-auto-newline-analysis t)) - (setq ln-syntax (c-guess-basic-syntax))) - (if c-syntactic-indentation - (c-indent-line ln-syntax)) - - (when c-auto-newline - (backward-char) - (setq br-syntax (c-point-syntax) - newlines (c-brace-newlines br-syntax)) - - ;; Insert the BEFORE newline, if wanted, and reindent the newline. - (if (and (memq 'before newlines) - (> (current-column) (current-indentation))) - (if c-syntactic-indentation - ;; Only a plain newline for now - it's indented - ;; after the cleanups when the line has its final - ;; appearance. - (newline) - (c-newline-and-indent))) + (let (post-self-insert-hook) ; the only way to get defined functionality + ; from `self-insert-command'. + (self-insert-command (prefix-numeric-value arg))) + + ;; Emulate `electric-pair-mode'. + (when (and (boundp 'electric-pair-mode) + electric-pair-mode) + (let ((size (buffer-size)) + (c-in-electric-pair-functionality t) + post-self-insert-hook) + (electric-pair-post-self-insert-function) + (setq got-pair-} (and at-eol + (eq (c-last-command-char) ?{) + (eq (char-after) ?})) + electric-pair-deletion (< (buffer-size) size)))) + + ;; Perform any required CC Mode electric actions. + (cond + ((or literal arg (not c-electric-flag) active-region)) + ((not at-eol) + (c-indent-line)) + (electric-pair-deletion + (c-indent-line) + (c-do-brace-electrics 'ignore nil)) + (t (c-do-brace-electrics nil nil) + (when got-pair-} + (save-excursion (forward-char) - - ;; `syntax' is the syntactic context of the line which ends up - ;; with the brace on it. - (setq syntax (if (memq 'before newlines) br-syntax ln-syntax)) - - ;; Do all appropriate clean ups - (let ((here (point)) - (pos (- (point-max) (point))) - mbeg mend - ) - - ;; `}': clean up empty defun braces - (when (c-save-buffer-state () - (and (memq 'empty-defun-braces c-cleanup-list) - (eq (c-last-command-char) ?\}) - (c-intersect-lists '(defun-close class-close inline-close) - syntax) - (progn - (forward-char -1) - (c-skip-ws-backward) - (eq (char-before) ?\{)) - ;; make sure matching open brace isn't in a comment - (not (c-in-literal)))) - (delete-region (point) (1- here)) - (setq here (- (point-max) pos))) - (goto-char here) - - ;; `}': compact to a one-liner defun? - (save-match-data - (when - (and (eq (c-last-command-char) ?\}) - (memq 'one-liner-defun c-cleanup-list) - (c-intersect-lists '(defun-close) syntax) - (c-try-one-liner)) - (setq here (- (point-max) pos)))) - - ;; `{': clean up brace-else-brace and brace-elseif-brace - (when (eq (c-last-command-char) ?\{) - (cond - ((and (memq 'brace-else-brace c-cleanup-list) - (re-search-backward - (concat "}" - "\\([ \t\n]\\|\\\\\n\\)*" - "else" - "\\([ \t\n]\\|\\\\\n\\)*" - "{" - "\\=") - nil t)) - (delete-region (match-beginning 0) (match-end 0)) - (insert-and-inherit "} else {")) - ((and (memq 'brace-elseif-brace c-cleanup-list) - (progn - (goto-char (1- here)) - (setq mend (point)) - (c-skip-ws-backward) - (setq mbeg (point)) - (eq (char-before) ?\))) - (zerop (c-save-buffer-state nil (c-backward-token-2 1 t))) - (eq (char-after) ?\() - ; (progn - ; (setq tmp (point)) - (re-search-backward - (concat "}" - "\\([ \t\n]\\|\\\\\n\\)*" - "else" - "\\([ \t\n]\\|\\\\\n\\)+" - "if" - "\\([ \t\n]\\|\\\\\n\\)*" - "\\=") - nil t);) - ;(eq (match-end 0) tmp); - ) - (delete-region mbeg mend) - (goto-char mbeg) - (insert ?\ )))) - - (goto-char (- (point-max) pos)) - - ;; Indent the line after the cleanups since it might - ;; very well indent differently due to them, e.g. if - ;; c-indent-one-line-block is used together with the - ;; one-liner-defun cleanup. - (when c-syntactic-indentation - (c-indent-line))) - - ;; does a newline go after the brace? - (if (memq 'after newlines) - (c-newline-and-indent)) - )))) + (c-do-brace-electrics 'assume 'ignore)) + (c-indent-line)))) ;; blink the paren (and (eq (c-last-command-char) ?\}) @@ -903,7 +944,8 @@ c-electric-slash c-electric-flag (eq (c-last-command-char) ?/) (eq (char-before) (if literal ?* ?/)))) - (self-insert-command (prefix-numeric-value arg)) + (let (post-self-insert-hook) ; Disable random functionality. + (self-insert-command (prefix-numeric-value arg))) (if indentp (indent-according-to-mode)))) @@ -916,7 +958,8 @@ c-electric-star this indentation is inhibited." (interactive "*P") - (self-insert-command (prefix-numeric-value arg)) + (let (post-self-insert-hook) ; Disable random functionality. + (self-insert-command (prefix-numeric-value arg))) ;; if we are in a literal, or if arg is given do not reindent the ;; current line, unless this star introduces a comment-only line. (if (c-save-buffer-state () @@ -963,7 +1006,8 @@ c-electric-semi&comma (setq lim (c-most-enclosing-brace (c-parse-state)) literal (c-in-literal lim))) - (self-insert-command (prefix-numeric-value arg)) + (let (post-self-insert-hook) ; Disable random functionality. + (self-insert-command (prefix-numeric-value arg))) (if (and c-electric-flag (not literal) (not arg)) ;; do all cleanups and newline insertions if c-auto-newline is on. @@ -1032,7 +1076,8 @@ c-electric-colon newlines is-scope-op ;; shut this up (c-echo-syntactic-information-p nil)) - (self-insert-command (prefix-numeric-value arg)) + (let (post-self-insert-hook) ; Disable random functionality. + (self-insert-command (prefix-numeric-value arg))) ;; Any electric action? (if (and c-electric-flag (not literal) (not arg)) ;; Unless we're at EOL, only re-indentation happens. @@ -1125,7 +1170,8 @@ c-electric-lt-gt (let ((c-echo-syntactic-information-p nil) final-pos found-delim case-fold-search) - (self-insert-command (prefix-numeric-value arg)) + (let (post-self-insert-hook) ; Disable random functionality. + (self-insert-command (prefix-numeric-value arg))) (setq final-pos (point)) ;;;; 2010-01-31: There used to be code here to put a syntax-table text @@ -1190,7 +1236,9 @@ c-electric-paren ;; shut this up (c-echo-syntactic-information-p nil) case-fold-search) - (self-insert-command (prefix-numeric-value arg)) + (let (post-self-insert-hook) ; The only way to get defined functionality + ; from `self-insert-command'. + (self-insert-command (prefix-numeric-value arg))) (if (and (not arg) (not literal)) (let* ( ;; We want to inhibit blinking the paren since this will @@ -1239,6 +1287,12 @@ c-electric-paren (delete-region (match-beginning 0) (match-end 0)) (insert-and-inherit "} catch ("))) + ;; Apply `electric-pair-mode' stuff. + (when (and (boundp 'electric-pair-mode) + electric-pair-mode) + (let (post-self-insert-hook) + (electric-pair-post-self-insert-function))) + ;; Check for clean-ups at function calls. These two DON'T need ;; `c-electric-flag' or `c-syntactic-indentation' set. ;; Point is currently just after the inserted paren. -- Alan Mackenzie (Nuremberg, Germany).