Subject: Ensure quick substitution only occurs at start of line

From: Jay Kamat <[email-masked]>
Date: Fri, 22 Dec 2017 15:57:08 -0800

Hi!

I'm filing this separately from #29157, because I think that issue got a
bit overloaded with multiple eshell problems and is very hard to follow.

I recently noticed the changes in #29157, and I'm disappointed that we
came to the conclusion to disable history expansion completely. I find
it's rather useful, especially for things like:

$ mv one.txt two.txtt
# whoops!
$ mv !!:$ two.txt

This is preferred (in my opinion) over lisp functions to keep muscle
memory working between shells. If anything, I would suggest disabling
quick substitution (as I don't find it more useful than using history
directly most of the time)

I've created a patch to try to fix the bug found in #29157, which was:

> echo $PATH | sed "s/[^o]foo[^o]/bar/g"
> Unknown predicate character ‘b’

The fix is rather simple, it simply limits the quick substitution to the
start of the line only (as observed in bash, as Andreas noted in the
previous thread).

I hope that we reconsider the decision to disable history expansion by
default, it's a nice feature of eshell (which I have another patch I
would like to submit later to try to expand it's functionality a bit
more).

Please let me know if you think this is a poor way of solving this issue
(or if anything else seems wrong or missing), and I'll try to follow up.

Thanks,
-Jay

>From 2c14085989a1edb5d3420150dcf91bc0914f012b Mon Sep 17 00:00:00 2001
From: Jay Kamat <[email-masked]>
Date: Fri, 22 Dec 2017 15:34:44 -0800
Subject: [PATCH] Prevent expansion of quick substitutions when not at start of
 line

See bug #29157 for an initial report

* lisp/eshell/em-hist.el (eshell-expand-history-references): Calculate
  and send a start-of-line variable to eshell-history-reference
(eshell-history-reference): Use start-of-line variable to force
expansion of quick substitutions only on start of line
---
 lisp/eshell/em-hist.el | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/lisp/eshell/em-hist.el b/lisp/eshell/em-hist.el
index df462a7058..9561d8b988 100644
--- a/lisp/eshell/em-hist.el
+++ b/lisp/eshell/em-hist.el
@@ -588,8 +588,9 @@ eshell-expand-history-references
 	    (pose (nreverse (nth 2 result))))
 	(save-excursion
 	  (while textargs
-	    (let ((str (eshell-history-reference (car textargs))))
-	      (unless (eq str (car textargs))
+            (let ((str (eshell-history-reference (car textargs)
+                                                 (not (cdr-safe textargs)))))
+              (unless (eq str (car textargs))
 		(goto-char (car posb))
 		(insert-and-inherit str)
 		(delete-char (- (car pose) (car posb)))))
@@ -630,7 +631,7 @@ eshell-complete-history-reference
 		   (setq history (cdr history)))
 		 (cdr fhist)))))))
 
-(defun eshell-history-reference (reference)
+(defun eshell-history-reference (reference start-of-line)
   "Expand directory stack REFERENCE.
 The syntax used here was taken from the Bash info manual.
 Returns the resultant reference, or the same string REFERENCE if none
@@ -638,7 +639,8 @@ eshell-history-reference
   ;; `^string1^string2^'
   ;;      Quick Substitution.  Repeat the last command, replacing
   ;;      STRING1 with STRING2.  Equivalent to `!!:s/string1/string2/'
-  (if (and (eshell-using-module 'eshell-pred)
+  (if (and start-of-line
+           (eshell-using-module 'eshell-pred)
 	   (string-match "\\^\\([^^]+\\)\\^\\([^^]+\\)\\^?\\s-*$"
 			 reference))
       (setq reference (format "!!:s/%s/%s/"
-- 
2.11.0