Subject: bug#3226: here-document syntax inference and completion is broken

From: Mark Oteiza <[email-masked]>
Date: Sun, 27 Mar 2016 15:56:00 -0400

Philipp Stephani <[email-masked]> writes:

> Andrew Hyatt <[email-masked]> schrieb am Di., 26. Jan. 2016 um 06:16 Uhr:
>
>  Chong Yidong <[email-masked]> writes:
>
>  >> Cook yourself up a bash script and type a line of code like:
>  >> for i in $(/bin/vim <<
>  >>
>  >> A default emacs install will complete with "EOF\n\nEOF" (in a more
>  >>clever fashion so the rest of the line is undisturbed), and place the
>  >>cursor on the new blank line. This is incorrect behaviour, as it
>  >>breaks entry of BASH here-strings when the user's intent is still
>  >>ambiguous. This should only respond to things ending in, for example
>  >>"<< " or possibly "<<E", but certainly NOT just "<<".
>  >>
>  >> Further, emacs does something outright wrong: An attempt to fix the
>  >>situation by inserting another '<' between the "<<" and "EOF" +results
>  >>in ANOTHER pair of "EOF" being inserted in a syntactically invalid way.
>  >
>  > I fixed the second problem, but I don't see any easy way to solve the
>  > first one.
>  >
>  > (The difficulty of fixing the first bug is illustrative of the problems
>  > with individual major modes rolling their own "electric" completion
>  > facilities. It might be nice to have a minor mode that provides a
>  > unified framework for this kind of functionality; which may also need
>  > built-in support.)
>
>  Just a note that I've reproduced the original problem in Emacs 25, so it
>  appears to be unfixed at present.
>
> The behavior can be disabled using
>
> (add-hook 'sh-mode-hook (lambda () (sh-electric-here-document-mode 0)))
>
> I think this behavior should be customizable and off by default. 

I agree. In addition to breaking here strings, it also complicates
writing here docs not using sh-here-document-word, as well as here docs
with additional contents after <<TAG. For example, this is valid bash:

sed -f - <<EOF <(foo "$x") | bar
s/cat/dog/g
EOF

The behavior has been in place for a long time though (IIUC Stefan's
mail correctly, since emacs 23), so I am wary of applying the following:

diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el
index 4fb5835..324cbf1 100644
--- a/lisp/progmodes/sh-script.el
+++ b/lisp/progmodes/sh-script.el
@@ -1228,6 +1228,7 @@ defcustom sh-set-shell-hook nil
 (defcustom sh-mode-hook nil
   "Hook run by `sh-mode'."
   :type 'hook
+  :options '(sh-electric-here-document-mode)
   :group 'sh-script)
 
 (defcustom sh-learn-basic-offset nil
@@ -1616,7 +1617,8 @@ define-derived-mode sh-mode prog-mode "Shell-script"
 \\[sh-execute-region]	 Have optional header and region be executed in a subshell.
 
 `sh-electric-here-document-mode' controls whether insertion of two
-unquoted < insert a here document.
+unquoted < insert a here document.  You can enable this behavior by
+adding to `sh-mode-hook'.
 
 If you generally program a shell different from your login shell you can
 set `sh-shell-file' accordingly.  If your shell's file name doesn't correctly
@@ -1653,7 +1655,6 @@ define-derived-mode sh-mode prog-mode "Shell-script"
   (setq-local syntax-propertize-function #'sh-syntax-propertize-function)
   (add-hook 'syntax-propertize-extend-region-functions
             #'syntax-propertize-multiline 'append 'local)
-  (sh-electric-here-document-mode 1)
   (setq-local skeleton-pair-alist '((?` _ ?`)))
   (setq-local skeleton-pair-filter-function 'sh-quoted-p)
   (setq-local skeleton-further-elements