* bug#53371: 28.0.90; eshell completion error when trying to complete buffer name, args-out-of-range
@ 2022-01-19 17:35 Daniel Mendler
2023-02-02 6:40 ` Jim Porter
0 siblings, 1 reply; 6+ messages in thread
From: Daniel Mendler @ 2022-01-19 17:35 UTC (permalink / raw)
To: 53371
1. Start emacs -Q
2. M-x eshell
3. Enter "ls > #<" for buffer redirection.
4. Pressing TAB results in an args-out-of-range error, see below.
It seems that completing buffer names is not supported. This feature
would be nice to have. However in any case, no args-out-of-range error
should occur.
Debugger entered--Lisp error: (args-out-of-range 41 #<marker at 40 in
*eshell*>)
eshell-parse-arguments(41 #<marker at 40 in *eshell*>)
eshell-complete-parse-arguments()
pcomplete-parse-arguments(nil)
pcomplete-completions()
pcomplete-completions-at-point()
completion--capf-wrapper(pcomplete-completions-at-point all)
completion-at-point()
funcall-interactively(completion-at-point)
In GNU Emacs 28.0.90 (build 3, x86_64-pc-linux-gnu, GTK+ Version 3.24.5,
cairo version 1.16.0)
of 2022-01-01 built on projects
Repository revision: efb1c7ec379430f560c5b801969ae43023c52734
Repository branch: emacs-28
Windowing system distributor 'The X.Org Foundation', version 11.0.12004000
System Description: Debian GNU/Linux 10 (buster)
Configured using:
'configure --prefix=/home/user/emacs/install --with-cairo --with-json
--with-native-compilation --with-xwidgets'
Configured features:
CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GSETTINGS HARFBUZZ JPEG JSON
LIBOTF LIBSELINUX LIBXML2 MODULES NATIVE_COMP NOTIFY INOTIFY PDUMPER PNG
RSVG SECCOMP SOUND THREADS TIFF TOOLKIT_SCROLL_BARS X11 XDBE XIM XPM
XWIDGETS GTK3 ZLIB
Important settings:
value of $LANG: en_US.UTF-8
locale-coding-system: utf-8-unix
Major mode: Debugger
Minor modes in effect:
shell-dirtrack-mode: t
tooltip-mode: t
global-eldoc-mode: t
show-paren-mode: t
electric-indent-mode: t
mouse-wheel-mode: t
tool-bar-mode: t
menu-bar-mode: t
file-name-shadow-mode: t
global-font-lock-mode: t
font-lock-mode: t
blink-cursor-mode: t
auto-composition-mode: t
auto-encryption-mode: t
auto-compression-mode: t
buffer-read-only: t
line-number-mode: t
indent-tabs-mode: t
transient-mark-mode: t
Load-path shadows:
None found.
Features:
(shadow sort mail-extr help-fns radix-tree cl-print debug backtrace
help-mode find-func cus-start cus-load emacsbug message rmc puny dired
dired-loaddefs rfc822 mml mml-sec epa derived epg rfc6068 epg-config
gnus-util rmail rmail-loaddefs auth-source cl-seq eieio eieio-core
cl-macs eieio-loaddefs password-cache json map text-property-search
time-date subr-x mm-decode mm-bodies mm-encode mail-parse rfc2231
mailabbrev gmm-utils mailheader sendmail rfc2047 rfc2045 ietf-drums
mm-util mail-prsvr mail-utils em-unix em-term term disp-table shell
ehelp em-script em-prompt em-ls cl-loaddefs cl-lib em-hist em-pred
em-glob em-cmpl em-dirs esh-var pcomplete comint ansi-color ring
em-basic em-banner em-alias esh-mode eshell esh-cmd esh-ext esh-opt
esh-proc esh-io esh-arg esh-module esh-groups esh-util seq byte-opt gv
bytecomp byte-compile cconv iso-transl tooltip eldoc paren electric
uniquify ediff-hook vc-hooks lisp-float-type elisp-mode mwheel
term/x-win x-win term/common-win x-dnd tool-bar dnd fontset image
regexp-opt fringe tabulated-list replace newcomment text-mode lisp-mode
prog-mode register page tab-bar menu-bar rfn-eshadow isearch easymenu
timer select scroll-bar mouse jit-lock font-lock syntax font-core
term/tty-colors frame minibuffer cl-generic cham georgian utf-8-lang
misc-lang vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms
cp51932 hebrew greek romanian slovak czech european ethiopic indian
cyrillic chinese composite emoji-zwj charscript charprop case-table
epa-hook jka-cmpr-hook help simple abbrev obarray cl-preloaded nadvice
button loaddefs faces cus-face macroexp files window text-properties
overlay sha1 md5 base64 format env code-pages mule custom widget
hashtable-print-readable backquote threads xwidget-internal dbusbind
inotify dynamic-setting system-font-setting font-render-setting cairo
move-toolbar gtk x-toolkit x multi-tty make-network-process
native-compile emacs)
Memory information:
((conses 16 107969 13219)
(symbols 48 9769 0)
(strings 32 28542 1249)
(string-bytes 1 959554)
(vectors 16 18825)
(vector-slots 8 358993 17174)
(floats 8 32 60)
(intervals 56 315 0)
(buffers 992 12))
^ permalink raw reply [flat|nested] 6+ messages in thread
* bug#53371: 28.0.90; eshell completion error when trying to complete buffer name, args-out-of-range
2022-01-19 17:35 bug#53371: 28.0.90; eshell completion error when trying to complete buffer name, args-out-of-range Daniel Mendler
@ 2023-02-02 6:40 ` Jim Porter
2023-02-24 6:40 ` Jim Porter
0 siblings, 1 reply; 6+ messages in thread
From: Jim Porter @ 2023-02-02 6:40 UTC (permalink / raw)
To: mail, 53371
[-- Attachment #1: Type: text/plain, Size: 478 bytes --]
On 1/19/2022 9:35 AM, Daniel Mendler wrote:
> 1. Start emacs -Q
> 2. M-x eshell
> 3. Enter "ls > #<" for buffer redirection.
> 4. Pressing TAB results in an args-out-of-range error, see below.
>
> It seems that completing buffer names is not supported. This feature
> would be nice to have. However in any case, no args-out-of-range error
> should occur.
Here's a patch for this, with a few tests. Note that it depends on my
patches in bug#61221, so it can't merge quite yet.
[-- Attachment #2: 0001-Add-support-for-completing-special-references-e.g.-b.patch --]
[-- Type: text/plain, Size: 11110 bytes --]
From 5099ffc8ee4ff1f821fa7da222351a6c40dd64a9 Mon Sep 17 00:00:00 2001
From: Jim Porter <jporterbugs@gmail.com>
Date: Tue, 24 Jan 2023 21:22:06 -0800
Subject: [PATCH] Add support for completing special references (e.g. buffers)
in Eshell
* lisp/eshell/em-cmpl.el (eshell-complete-parse-arguments): Handle
special references.
* lisp/eshell/em-arg.el (eshell-parse-special-reference): Ensure point
is just after the "#<" when incomplete, and handle backslash escapes
more thoroughly.
(eshell-complete-special-reference)
(eshell-complete-special-ref--exit): New functions.
* test/lisp/eshell/esh-arg-tests.el
(esh-arg-test/special-reference/default)
(esh-arg-test/special-reference/buffer)
(esh-arg-test/special-reference/special):
* test/lisp/eshell/em-cmpl-tests.el
(em-cmpl-test/special-ref-completion/type)
(em-cmpl-test/special-ref-completion/implicit-buffer)
(em-cmpl-test/special-ref-completion/buffer): New tests.
---
lisp/eshell/em-cmpl.el | 10 +++--
lisp/eshell/esh-arg.el | 63 +++++++++++++++++++++++++++----
test/lisp/eshell/em-cmpl-tests.el | 40 ++++++++++++++++++++
test/lisp/eshell/esh-arg-tests.el | 30 +++++++++++++++
4 files changed, 131 insertions(+), 12 deletions(-)
diff --git a/lisp/eshell/em-cmpl.el b/lisp/eshell/em-cmpl.el
index 380ecd0b91d..b2a69d70f3a 100644
--- a/lisp/eshell/em-cmpl.el
+++ b/lisp/eshell/em-cmpl.el
@@ -319,7 +319,7 @@ eshell-complete-parse-arguments
(eshell--pcomplete-insert-tab))
(let ((end (point-marker))
(begin (save-excursion (beginning-of-line) (point)))
- args posns delim)
+ args posns delim incomplete-arg)
(when (and pcomplete-allow-modifications
(memq this-command '(pcomplete-expand
pcomplete-expand-and-complete)))
@@ -334,10 +334,11 @@ eshell-complete-parse-arguments
(cond ((member (car delim) '("{" "${" "$<"))
(setq begin (1+ (cadr delim))
args (eshell-parse-arguments begin end)))
- ((member (car delim) '("$'" "$\""))
+ ((member (car delim) '("$'" "$\"" "#<"))
;; Add the (incomplete) argument to our arguments, and
;; note its position.
- (setq args (append (nth 2 delim) (list (car delim))))
+ (setq args (append (nth 2 delim) (list (car delim)))
+ incomplete-arg t)
(push (- (nth 1 delim) 2) posns))
((member (car delim) '("(" "$("))
(throw 'pcompleted (elisp-completion-at-point)))
@@ -364,7 +365,8 @@ eshell-complete-parse-arguments
(setq args (nthcdr (1+ new-start) args)
posns (nthcdr (1+ new-start) posns))))
(cl-assert (= (length args) (length posns)))
- (when (and args (eq (char-syntax (char-before end)) ? )
+ (when (and args (not incomplete-arg)
+ (eq (char-syntax (char-before end)) ? )
(not (eq (char-before (1- end)) ?\\)))
(nconc args (list ""))
(nconc posns (list (point))))
diff --git a/lisp/eshell/esh-arg.el b/lisp/eshell/esh-arg.el
index cb0b2e0938c..6acc6c812d3 100644
--- a/lisp/eshell/esh-arg.el
+++ b/lisp/eshell/esh-arg.el
@@ -28,6 +28,9 @@
;;; Code:
(require 'esh-util)
+(require 'esh-module)
+
+(require 'pcomplete)
(eval-when-compile
(require 'cl-lib))
@@ -175,7 +178,11 @@ eshell-arg-initialize
"Initialize the argument parsing code."
(eshell-arg-mode)
(setq-local eshell-inside-quote-regexp nil)
- (setq-local eshell-outside-quote-regexp nil))
+ (setq-local eshell-outside-quote-regexp nil)
+
+ (when (eshell-using-module 'eshell-cmpl)
+ (add-hook 'pcomplete-try-first-hook
+ #'eshell-complete-special-reference nil t)))
(defun eshell-insert-buffer-name (buffer-name)
"Insert BUFFER-NAME into the current buffer at point."
@@ -506,21 +513,28 @@ eshell-parse-special-reference
\"buffer\"."
(when (and (not eshell-current-argument)
(not eshell-current-quoted)
- (looking-at "#<\\(\\(buffer\\|process\\)\\s-\\)?"))
+ (looking-at (rx "#<" (? (group (or "buffer" "process"))
+ space))))
(let ((here (point)))
(goto-char (match-end 0)) ;; Go to the end of the match.
- (let ((buffer-p (if (match-string 1)
- (string= (match-string 2) "buffer")
+ (let ((buffer-p (if (match-beginning 1)
+ (equal (match-string 1) "buffer")
t)) ;; buffer-p is non-nil by default.
(end (eshell-find-delimiter ?\< ?\>)))
(when (not end)
+ (when (match-beginning 1)
+ (goto-char (match-beginning 1)))
(throw 'eshell-incomplete "#<"))
(if (eshell-arg-delimiter (1+ end))
(prog1
- (list (if buffer-p 'get-buffer-create 'get-process)
- (replace-regexp-in-string
- (rx "\\" (group (or "\\" "<" ">"))) "\\1"
- (buffer-substring-no-properties (point) end)))
+ (list (if buffer-p #'get-buffer-create #'get-process)
+ ;; FIXME: We should probably parse this as a
+ ;; real Eshell argument so that we get the
+ ;; benefits of quoting, variable-expansion, etc.
+ (string-trim-right
+ (replace-regexp-in-string
+ (rx "\\" (group anychar)) "\\1"
+ (buffer-substring-no-properties (point) end))))
(goto-char (1+ end)))
(ignore (goto-char here)))))))
@@ -574,5 +588,38 @@ eshell-prepare-splice
(when splicep
grouped-args)))
+;;;_* Special ref completion
+
+(defun eshell-complete-special-reference ()
+ "If there is a special reference, complete it."
+ (let ((arg (pcomplete-actual-arg)))
+ (when (string-match
+ (rx string-start
+ "#<" (? (group (or "buffer" "process")) space)
+ (group (* anychar))
+ string-end)
+ arg)
+ (let ((all-results (if (equal (match-string 1 arg) "process")
+ (mapcar #'process-name (process-list))
+ (mapcar #'buffer-name (buffer-list)))))
+ (unless (match-beginning 1)
+ ;; Include the special reference types as completion options.
+ (setq all-results (append '("buffer" "process") all-results)))
+ (setq pcomplete-stub (replace-regexp-in-string
+ (rx "\\" (group anychar)) "\\1"
+ (substring arg (match-beginning 2)))
+ pcomplete-exit-function #'eshell-complete-special-ref--exit)
+ (throw 'pcomplete-completions
+ (all-completions pcomplete-stub all-results))))))
+
+(defun eshell-complete-special-ref--exit (value status)
+ "An exit function for completing Eshell special references.
+STATUS is a symbol representing the state of the completion."
+ (when (and (eq status 'finished)
+ (not (member value '("buffer" "process"))))
+ (if (looking-at ">")
+ (goto-char (match-end 0))
+ (insert ">"))))
+
(provide 'esh-arg)
;;; esh-arg.el ends here
diff --git a/test/lisp/eshell/em-cmpl-tests.el b/test/lisp/eshell/em-cmpl-tests.el
index ecab7332822..abc39721d9b 100644
--- a/test/lisp/eshell/em-cmpl-tests.el
+++ b/test/lisp/eshell/em-cmpl-tests.el
@@ -176,6 +176,46 @@ em-cmpl-test/lisp-function-completion
(should (equal (eshell-insert-and-complete "echo (eshell/ech")
"echo (eshell/echo"))))
+(ert-deftest em-cmpl-test/special-ref-completion/type ()
+ "Test completion of the start of special references like \"#<buffer\".
+See <lisp/eshell/esh-arg.el>."
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<buf")
+ "echo hi > #<buffer ")))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<proc")
+ "echo hi > #<process "))))
+
+(ert-deftest em-cmpl-test/special-ref-completion/implicit-buffer ()
+ "Test completion of special references like \"#<buf>\".
+See <lisp/eshell/esh-arg.el>."
+ (let (bufname)
+ (with-temp-buffer
+ (setq bufname (rename-buffer "my-buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<my-buf")
+ (format "echo hi > #<%s> " bufname))))
+ (setq bufname (rename-buffer "another buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<anoth")
+ (format "echo hi > #<%s> "
+ (string-replace " " "\\ " bufname))))))))
+
+(ert-deftest em-cmpl-test/special-ref-completion/buffer ()
+ "Test completion of special references like \"#<buffer buf>\".
+See <lisp/eshell/esh-arg.el>."
+ (let (bufname)
+ (with-temp-buffer
+ (setq bufname (rename-buffer "my-buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<buffer my-buf")
+ (format "echo hi > #<buffer %s> " bufname))))
+ (setq bufname (rename-buffer "another buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<buffer anoth")
+ (format "echo hi > #<buffer %s> "
+ (string-replace " " "\\ " bufname))))))))
+
(ert-deftest em-cmpl-test/variable-ref-completion ()
"Test completion of variable references like \"$var\".
See <lisp/eshell/esh-var.el>."
diff --git a/test/lisp/eshell/esh-arg-tests.el b/test/lisp/eshell/esh-arg-tests.el
index 918ad3a949f..c883db3907f 100644
--- a/test/lisp/eshell/esh-arg-tests.el
+++ b/test/lisp/eshell/esh-arg-tests.el
@@ -102,4 +102,34 @@ esh-arg-test/escape-quoted/newline
(eshell-match-command-output "echo \"hi\\\nthere\""
"hithere\n")))
+(ert-deftest esh-arg-test/special-reference/default ()
+ "Test that \"#<buf>\" refers to the buffer \"buf\"."
+ (with-temp-buffer
+ (rename-buffer "my-buffer" t)
+ (eshell-command-result-equal
+ (format "echo #<%s>" (buffer-name))
+ (current-buffer))))
+
+(ert-deftest esh-arg-test/special-reference/buffer ()
+ "Test that \"#<buffer buf>\" refers to the buffer \"buf\"."
+ (with-temp-buffer
+ (rename-buffer "my-buffer" t)
+ (eshell-command-result-equal
+ (format "echo #<buffer %s>" (buffer-name))
+ (current-buffer))))
+
+(ert-deftest esh-arg-test/special-reference/special ()
+ "Test that \"#<...>\" works correctly when escaping special characters."
+ (with-temp-buffer
+ (rename-buffer "<my buffer>" t)
+ (let ((escaped-bufname (replace-regexp-in-string
+ (rx (group (or "\\" "<" ">" space))) "\\\\\\1"
+ (buffer-name))))
+ (eshell-command-result-equal
+ (format "echo #<%s>" escaped-bufname)
+ (current-buffer))
+ (eshell-command-result-equal
+ (format "echo #<buffer %s>" escaped-bufname)
+ (current-buffer)))))
+
;; esh-arg-tests.el ends here
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* bug#53371: 28.0.90; eshell completion error when trying to complete buffer name, args-out-of-range
2023-02-02 6:40 ` Jim Porter
@ 2023-02-24 6:40 ` Jim Porter
2023-02-24 15:09 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
0 siblings, 1 reply; 6+ messages in thread
From: Jim Porter @ 2023-02-24 6:40 UTC (permalink / raw)
To: mail, 53371; +Cc: monnier
[-- Attachment #1: Type: text/plain, Size: 262 bytes --]
X-Debbugs-Cc: monnier@iro.umontreal.ca
On 2/1/2023 10:40 PM, Jim Porter wrote:
> Here's a patch for this, with a few tests. Note that it depends on my
> patches in bug#61221, so it can't merge quite yet.
Now that bug#61221 has merged, here's an updated patch.
[-- Attachment #2: 0001-Add-support-for-completing-special-references-e.g.-b.patch --]
[-- Type: text/plain, Size: 11191 bytes --]
From 461faa03f4d63e71383c4430d2d0be04a553e003 Mon Sep 17 00:00:00 2001
From: Jim Porter <jporterbugs@gmail.com>
Date: Tue, 24 Jan 2023 21:22:06 -0800
Subject: [PATCH] Add support for completing special references (e.g. buffers)
in Eshell
* lisp/eshell/em-cmpl.el (eshell-complete-parse-arguments): Handle
special references.
* lisp/eshell/em-arg.el (eshell-parse-special-reference): Ensure point
is just after the "#<" when incomplete, and handle backslash escapes
more thoroughly.
(eshell-complete-special-reference): New function.
* test/lisp/eshell/esh-arg-tests.el
(esh-arg-test/special-reference/default)
(esh-arg-test/special-reference/buffer)
(esh-arg-test/special-reference/special):
* test/lisp/eshell/em-cmpl-tests.el
(em-cmpl-test/special-ref-completion/type)
(em-cmpl-test/special-ref-completion/implicit-buffer)
(em-cmpl-test/special-ref-completion/buffer): New tests.
---
lisp/eshell/em-cmpl.el | 10 +++--
lisp/eshell/esh-arg.el | 66 +++++++++++++++++++++++++++----
test/lisp/eshell/em-cmpl-tests.el | 40 +++++++++++++++++++
test/lisp/eshell/esh-arg-tests.el | 30 ++++++++++++++
4 files changed, 134 insertions(+), 12 deletions(-)
diff --git a/lisp/eshell/em-cmpl.el b/lisp/eshell/em-cmpl.el
index 5dfd10d6e4c..b65652019d4 100644
--- a/lisp/eshell/em-cmpl.el
+++ b/lisp/eshell/em-cmpl.el
@@ -317,7 +317,7 @@ eshell-complete-parse-arguments
(eshell--pcomplete-insert-tab))
(let ((end (point-marker))
(begin (save-excursion (beginning-of-line) (point)))
- args posns delim)
+ args posns delim incomplete-arg)
(when (and pcomplete-allow-modifications
(memq this-command '(pcomplete-expand
pcomplete-expand-and-complete)))
@@ -332,10 +332,11 @@ eshell-complete-parse-arguments
(cond ((member (car delim) '("{" "${" "$<"))
(setq begin (1+ (cadr delim))
args (eshell-parse-arguments begin end)))
- ((member (car delim) '("$'" "$\""))
+ ((member (car delim) '("$'" "$\"" "#<"))
;; Add the (incomplete) argument to our arguments, and
;; note its position.
- (setq args (append (nth 2 delim) (list (car delim))))
+ (setq args (append (nth 2 delim) (list (car delim)))
+ incomplete-arg t)
(push (- (nth 1 delim) 2) posns))
((member (car delim) '("(" "$("))
(throw 'pcompleted (elisp-completion-at-point)))
@@ -362,7 +363,8 @@ eshell-complete-parse-arguments
(setq args (nthcdr (1+ new-start) args)
posns (nthcdr (1+ new-start) posns))))
(cl-assert (= (length args) (length posns)))
- (when (and args (eq (char-syntax (char-before end)) ? )
+ (when (and args (not incomplete-arg)
+ (eq (char-syntax (char-before end)) ? )
(not (eq (char-before (1- end)) ?\\)))
(nconc args (list ""))
(nconc posns (list (point))))
diff --git a/lisp/eshell/esh-arg.el b/lisp/eshell/esh-arg.el
index cb0b2e0938c..9bb692f7d03 100644
--- a/lisp/eshell/esh-arg.el
+++ b/lisp/eshell/esh-arg.el
@@ -28,6 +28,9 @@
;;; Code:
(require 'esh-util)
+(require 'esh-module)
+
+(require 'pcomplete)
(eval-when-compile
(require 'cl-lib))
@@ -175,7 +178,11 @@ eshell-arg-initialize
"Initialize the argument parsing code."
(eshell-arg-mode)
(setq-local eshell-inside-quote-regexp nil)
- (setq-local eshell-outside-quote-regexp nil))
+ (setq-local eshell-outside-quote-regexp nil)
+
+ (when (eshell-using-module 'eshell-cmpl)
+ (add-hook 'pcomplete-try-first-hook
+ #'eshell-complete-special-reference nil t)))
(defun eshell-insert-buffer-name (buffer-name)
"Insert BUFFER-NAME into the current buffer at point."
@@ -506,21 +513,28 @@ eshell-parse-special-reference
\"buffer\"."
(when (and (not eshell-current-argument)
(not eshell-current-quoted)
- (looking-at "#<\\(\\(buffer\\|process\\)\\s-\\)?"))
+ (looking-at (rx "#<" (? (group (or "buffer" "process"))
+ space))))
(let ((here (point)))
(goto-char (match-end 0)) ;; Go to the end of the match.
- (let ((buffer-p (if (match-string 1)
- (string= (match-string 2) "buffer")
+ (let ((buffer-p (if (match-beginning 1)
+ (equal (match-string 1) "buffer")
t)) ;; buffer-p is non-nil by default.
(end (eshell-find-delimiter ?\< ?\>)))
(when (not end)
+ (when (match-beginning 1)
+ (goto-char (match-beginning 1)))
(throw 'eshell-incomplete "#<"))
(if (eshell-arg-delimiter (1+ end))
(prog1
- (list (if buffer-p 'get-buffer-create 'get-process)
- (replace-regexp-in-string
- (rx "\\" (group (or "\\" "<" ">"))) "\\1"
- (buffer-substring-no-properties (point) end)))
+ (list (if buffer-p #'get-buffer-create #'get-process)
+ ;; FIXME: We should probably parse this as a
+ ;; real Eshell argument so that we get the
+ ;; benefits of quoting, variable-expansion, etc.
+ (string-trim-right
+ (replace-regexp-in-string
+ (rx "\\" (group anychar)) "\\1"
+ (buffer-substring-no-properties (point) end))))
(goto-char (1+ end)))
(ignore (goto-char here)))))))
@@ -574,5 +588,41 @@ eshell-prepare-splice
(when splicep
grouped-args)))
+;;;_* Special ref completion
+
+(defun eshell-complete-special-reference ()
+ "If there is a special reference, complete it."
+ (let ((arg (pcomplete-actual-arg)))
+ (when (string-match
+ (rx string-start
+ "#<" (? (group (or "buffer" "process")) space)
+ (group (* anychar))
+ string-end)
+ arg)
+ (let ((all-results (if (equal (match-string 1 arg) "process")
+ (mapcar #'process-name (process-list))
+ (mapcar #'buffer-name (buffer-list))))
+ (saw-type (match-beginning 1)))
+ (unless saw-type
+ ;; Include the special reference types as completion options.
+ (setq all-results (append '("buffer" "process") all-results)))
+ (setq pcomplete-stub (replace-regexp-in-string
+ (rx "\\" (group anychar)) "\\1"
+ (substring arg (match-beginning 2))))
+ ;; When finished with completion, add a trailing ">" (unless
+ ;; we just completed the initial "buffer" or "process"
+ ;; keyword).
+ (add-function
+ :before (var pcomplete-exit-function)
+ (lambda (value status)
+ (when (and (eq status 'finished)
+ (or saw-type
+ (not (member value '("buffer" "process")))))
+ (if (looking-at ">")
+ (goto-char (match-end 0))
+ (insert ">")))))
+ (throw 'pcomplete-completions
+ (all-completions pcomplete-stub all-results))))))
+
(provide 'esh-arg)
;;; esh-arg.el ends here
diff --git a/test/lisp/eshell/em-cmpl-tests.el b/test/lisp/eshell/em-cmpl-tests.el
index ecab7332822..abc39721d9b 100644
--- a/test/lisp/eshell/em-cmpl-tests.el
+++ b/test/lisp/eshell/em-cmpl-tests.el
@@ -176,6 +176,46 @@ em-cmpl-test/lisp-function-completion
(should (equal (eshell-insert-and-complete "echo (eshell/ech")
"echo (eshell/echo"))))
+(ert-deftest em-cmpl-test/special-ref-completion/type ()
+ "Test completion of the start of special references like \"#<buffer\".
+See <lisp/eshell/esh-arg.el>."
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<buf")
+ "echo hi > #<buffer ")))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<proc")
+ "echo hi > #<process "))))
+
+(ert-deftest em-cmpl-test/special-ref-completion/implicit-buffer ()
+ "Test completion of special references like \"#<buf>\".
+See <lisp/eshell/esh-arg.el>."
+ (let (bufname)
+ (with-temp-buffer
+ (setq bufname (rename-buffer "my-buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<my-buf")
+ (format "echo hi > #<%s> " bufname))))
+ (setq bufname (rename-buffer "another buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<anoth")
+ (format "echo hi > #<%s> "
+ (string-replace " " "\\ " bufname))))))))
+
+(ert-deftest em-cmpl-test/special-ref-completion/buffer ()
+ "Test completion of special references like \"#<buffer buf>\".
+See <lisp/eshell/esh-arg.el>."
+ (let (bufname)
+ (with-temp-buffer
+ (setq bufname (rename-buffer "my-buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<buffer my-buf")
+ (format "echo hi > #<buffer %s> " bufname))))
+ (setq bufname (rename-buffer "another buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<buffer anoth")
+ (format "echo hi > #<buffer %s> "
+ (string-replace " " "\\ " bufname))))))))
+
(ert-deftest em-cmpl-test/variable-ref-completion ()
"Test completion of variable references like \"$var\".
See <lisp/eshell/esh-var.el>."
diff --git a/test/lisp/eshell/esh-arg-tests.el b/test/lisp/eshell/esh-arg-tests.el
index 918ad3a949f..c883db3907f 100644
--- a/test/lisp/eshell/esh-arg-tests.el
+++ b/test/lisp/eshell/esh-arg-tests.el
@@ -102,4 +102,34 @@ esh-arg-test/escape-quoted/newline
(eshell-match-command-output "echo \"hi\\\nthere\""
"hithere\n")))
+(ert-deftest esh-arg-test/special-reference/default ()
+ "Test that \"#<buf>\" refers to the buffer \"buf\"."
+ (with-temp-buffer
+ (rename-buffer "my-buffer" t)
+ (eshell-command-result-equal
+ (format "echo #<%s>" (buffer-name))
+ (current-buffer))))
+
+(ert-deftest esh-arg-test/special-reference/buffer ()
+ "Test that \"#<buffer buf>\" refers to the buffer \"buf\"."
+ (with-temp-buffer
+ (rename-buffer "my-buffer" t)
+ (eshell-command-result-equal
+ (format "echo #<buffer %s>" (buffer-name))
+ (current-buffer))))
+
+(ert-deftest esh-arg-test/special-reference/special ()
+ "Test that \"#<...>\" works correctly when escaping special characters."
+ (with-temp-buffer
+ (rename-buffer "<my buffer>" t)
+ (let ((escaped-bufname (replace-regexp-in-string
+ (rx (group (or "\\" "<" ">" space))) "\\\\\\1"
+ (buffer-name))))
+ (eshell-command-result-equal
+ (format "echo #<%s>" escaped-bufname)
+ (current-buffer))
+ (eshell-command-result-equal
+ (format "echo #<buffer %s>" escaped-bufname)
+ (current-buffer)))))
+
;; esh-arg-tests.el ends here
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* bug#53371: 28.0.90; eshell completion error when trying to complete buffer name, args-out-of-range
2023-02-24 6:40 ` Jim Porter
@ 2023-02-24 15:09 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-02-24 17:53 ` Jim Porter
0 siblings, 1 reply; 6+ messages in thread
From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-02-24 15:09 UTC (permalink / raw)
To: Jim Porter; +Cc: 53371, mail
> + (let ((buffer-p (if (match-beginning 1)
> + (equal (match-string 1) "buffer")
> t)) ;; buffer-p is non-nil by default.
The comment just describes what the code does but it would be more
useful to explain why.
Stefan
^ permalink raw reply [flat|nested] 6+ messages in thread
* bug#53371: 28.0.90; eshell completion error when trying to complete buffer name, args-out-of-range
2023-02-24 15:09 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-02-24 17:53 ` Jim Porter
2023-03-12 3:35 ` Jim Porter
0 siblings, 1 reply; 6+ messages in thread
From: Jim Porter @ 2023-02-24 17:53 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 53371, mail
[-- Attachment #1: Type: text/plain, Size: 407 bytes --]
On 2/24/2023 7:09 AM, Stefan Monnier via Bug reports for GNU Emacs, the
Swiss army knife of text editors wrote:
>> + (let ((buffer-p (if (match-beginning 1)
>> + (equal (match-string 1) "buffer")
>> t)) ;; buffer-p is non-nil by default.
>
> The comment just describes what the code does but it would be more
> useful to explain why.
Thanks, fixed.
[-- Attachment #2: 0001-Add-support-for-completing-special-references-e.g.-b.patch --]
[-- Type: text/plain, Size: 11268 bytes --]
From 14fd1282ed416fb31c8b49a99297a231dcf2bf26 Mon Sep 17 00:00:00 2001
From: Jim Porter <jporterbugs@gmail.com>
Date: Tue, 24 Jan 2023 21:22:06 -0800
Subject: [PATCH] Add support for completing special references (e.g. buffers)
in Eshell
* lisp/eshell/em-cmpl.el (eshell-complete-parse-arguments): Handle
special references.
* lisp/eshell/em-arg.el (eshell-parse-special-reference): Ensure point
is just after the "#<" when incomplete, and handle backslash escapes
more thoroughly.
(eshell-complete-special-reference): New function.
* test/lisp/eshell/esh-arg-tests.el
(esh-arg-test/special-reference/default)
(esh-arg-test/special-reference/buffer)
(esh-arg-test/special-reference/special):
* test/lisp/eshell/em-cmpl-tests.el
(em-cmpl-test/special-ref-completion/type)
(em-cmpl-test/special-ref-completion/implicit-buffer)
(em-cmpl-test/special-ref-completion/buffer): New tests.
---
lisp/eshell/em-cmpl.el | 10 +++--
lisp/eshell/esh-arg.el | 68 +++++++++++++++++++++++++++----
test/lisp/eshell/em-cmpl-tests.el | 40 ++++++++++++++++++
test/lisp/eshell/esh-arg-tests.el | 30 ++++++++++++++
4 files changed, 135 insertions(+), 13 deletions(-)
diff --git a/lisp/eshell/em-cmpl.el b/lisp/eshell/em-cmpl.el
index 5dfd10d6e4c..b65652019d4 100644
--- a/lisp/eshell/em-cmpl.el
+++ b/lisp/eshell/em-cmpl.el
@@ -317,7 +317,7 @@ eshell-complete-parse-arguments
(eshell--pcomplete-insert-tab))
(let ((end (point-marker))
(begin (save-excursion (beginning-of-line) (point)))
- args posns delim)
+ args posns delim incomplete-arg)
(when (and pcomplete-allow-modifications
(memq this-command '(pcomplete-expand
pcomplete-expand-and-complete)))
@@ -332,10 +332,11 @@ eshell-complete-parse-arguments
(cond ((member (car delim) '("{" "${" "$<"))
(setq begin (1+ (cadr delim))
args (eshell-parse-arguments begin end)))
- ((member (car delim) '("$'" "$\""))
+ ((member (car delim) '("$'" "$\"" "#<"))
;; Add the (incomplete) argument to our arguments, and
;; note its position.
- (setq args (append (nth 2 delim) (list (car delim))))
+ (setq args (append (nth 2 delim) (list (car delim)))
+ incomplete-arg t)
(push (- (nth 1 delim) 2) posns))
((member (car delim) '("(" "$("))
(throw 'pcompleted (elisp-completion-at-point)))
@@ -362,7 +363,8 @@ eshell-complete-parse-arguments
(setq args (nthcdr (1+ new-start) args)
posns (nthcdr (1+ new-start) posns))))
(cl-assert (= (length args) (length posns)))
- (when (and args (eq (char-syntax (char-before end)) ? )
+ (when (and args (not incomplete-arg)
+ (eq (char-syntax (char-before end)) ? )
(not (eq (char-before (1- end)) ?\\)))
(nconc args (list ""))
(nconc posns (list (point))))
diff --git a/lisp/eshell/esh-arg.el b/lisp/eshell/esh-arg.el
index cb0b2e0938c..aa1e8f77ea5 100644
--- a/lisp/eshell/esh-arg.el
+++ b/lisp/eshell/esh-arg.el
@@ -28,6 +28,9 @@
;;; Code:
(require 'esh-util)
+(require 'esh-module)
+
+(require 'pcomplete)
(eval-when-compile
(require 'cl-lib))
@@ -175,7 +178,11 @@ eshell-arg-initialize
"Initialize the argument parsing code."
(eshell-arg-mode)
(setq-local eshell-inside-quote-regexp nil)
- (setq-local eshell-outside-quote-regexp nil))
+ (setq-local eshell-outside-quote-regexp nil)
+
+ (when (eshell-using-module 'eshell-cmpl)
+ (add-hook 'pcomplete-try-first-hook
+ #'eshell-complete-special-reference nil t)))
(defun eshell-insert-buffer-name (buffer-name)
"Insert BUFFER-NAME into the current buffer at point."
@@ -506,21 +513,28 @@ eshell-parse-special-reference
\"buffer\"."
(when (and (not eshell-current-argument)
(not eshell-current-quoted)
- (looking-at "#<\\(\\(buffer\\|process\\)\\s-\\)?"))
+ (looking-at (rx "#<" (? (group (or "buffer" "process"))
+ space))))
(let ((here (point)))
(goto-char (match-end 0)) ;; Go to the end of the match.
- (let ((buffer-p (if (match-string 1)
- (string= (match-string 2) "buffer")
- t)) ;; buffer-p is non-nil by default.
+ (let ((buffer-p (if (match-beginning 1)
+ (equal (match-string 1) "buffer")
+ t)) ; With no type keyword, assume we want a buffer.
(end (eshell-find-delimiter ?\< ?\>)))
(when (not end)
+ (when (match-beginning 1)
+ (goto-char (match-beginning 1)))
(throw 'eshell-incomplete "#<"))
(if (eshell-arg-delimiter (1+ end))
(prog1
- (list (if buffer-p 'get-buffer-create 'get-process)
- (replace-regexp-in-string
- (rx "\\" (group (or "\\" "<" ">"))) "\\1"
- (buffer-substring-no-properties (point) end)))
+ (list (if buffer-p #'get-buffer-create #'get-process)
+ ;; FIXME: We should probably parse this as a
+ ;; real Eshell argument so that we get the
+ ;; benefits of quoting, variable-expansion, etc.
+ (string-trim-right
+ (replace-regexp-in-string
+ (rx "\\" (group anychar)) "\\1"
+ (buffer-substring-no-properties (point) end))))
(goto-char (1+ end)))
(ignore (goto-char here)))))))
@@ -574,5 +588,41 @@ eshell-prepare-splice
(when splicep
grouped-args)))
+;;;_* Special ref completion
+
+(defun eshell-complete-special-reference ()
+ "If there is a special reference, complete it."
+ (let ((arg (pcomplete-actual-arg)))
+ (when (string-match
+ (rx string-start
+ "#<" (? (group (or "buffer" "process")) space)
+ (group (* anychar))
+ string-end)
+ arg)
+ (let ((all-results (if (equal (match-string 1 arg) "process")
+ (mapcar #'process-name (process-list))
+ (mapcar #'buffer-name (buffer-list))))
+ (saw-type (match-beginning 1)))
+ (unless saw-type
+ ;; Include the special reference types as completion options.
+ (setq all-results (append '("buffer" "process") all-results)))
+ (setq pcomplete-stub (replace-regexp-in-string
+ (rx "\\" (group anychar)) "\\1"
+ (substring arg (match-beginning 2))))
+ ;; When finished with completion, add a trailing ">" (unless
+ ;; we just completed the initial "buffer" or "process"
+ ;; keyword).
+ (add-function
+ :before (var pcomplete-exit-function)
+ (lambda (value status)
+ (when (and (eq status 'finished)
+ (or saw-type
+ (not (member value '("buffer" "process")))))
+ (if (looking-at ">")
+ (goto-char (match-end 0))
+ (insert ">")))))
+ (throw 'pcomplete-completions
+ (all-completions pcomplete-stub all-results))))))
+
(provide 'esh-arg)
;;; esh-arg.el ends here
diff --git a/test/lisp/eshell/em-cmpl-tests.el b/test/lisp/eshell/em-cmpl-tests.el
index ecab7332822..abc39721d9b 100644
--- a/test/lisp/eshell/em-cmpl-tests.el
+++ b/test/lisp/eshell/em-cmpl-tests.el
@@ -176,6 +176,46 @@ em-cmpl-test/lisp-function-completion
(should (equal (eshell-insert-and-complete "echo (eshell/ech")
"echo (eshell/echo"))))
+(ert-deftest em-cmpl-test/special-ref-completion/type ()
+ "Test completion of the start of special references like \"#<buffer\".
+See <lisp/eshell/esh-arg.el>."
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<buf")
+ "echo hi > #<buffer ")))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<proc")
+ "echo hi > #<process "))))
+
+(ert-deftest em-cmpl-test/special-ref-completion/implicit-buffer ()
+ "Test completion of special references like \"#<buf>\".
+See <lisp/eshell/esh-arg.el>."
+ (let (bufname)
+ (with-temp-buffer
+ (setq bufname (rename-buffer "my-buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<my-buf")
+ (format "echo hi > #<%s> " bufname))))
+ (setq bufname (rename-buffer "another buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<anoth")
+ (format "echo hi > #<%s> "
+ (string-replace " " "\\ " bufname))))))))
+
+(ert-deftest em-cmpl-test/special-ref-completion/buffer ()
+ "Test completion of special references like \"#<buffer buf>\".
+See <lisp/eshell/esh-arg.el>."
+ (let (bufname)
+ (with-temp-buffer
+ (setq bufname (rename-buffer "my-buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<buffer my-buf")
+ (format "echo hi > #<buffer %s> " bufname))))
+ (setq bufname (rename-buffer "another buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<buffer anoth")
+ (format "echo hi > #<buffer %s> "
+ (string-replace " " "\\ " bufname))))))))
+
(ert-deftest em-cmpl-test/variable-ref-completion ()
"Test completion of variable references like \"$var\".
See <lisp/eshell/esh-var.el>."
diff --git a/test/lisp/eshell/esh-arg-tests.el b/test/lisp/eshell/esh-arg-tests.el
index 918ad3a949f..c883db3907f 100644
--- a/test/lisp/eshell/esh-arg-tests.el
+++ b/test/lisp/eshell/esh-arg-tests.el
@@ -102,4 +102,34 @@ esh-arg-test/escape-quoted/newline
(eshell-match-command-output "echo \"hi\\\nthere\""
"hithere\n")))
+(ert-deftest esh-arg-test/special-reference/default ()
+ "Test that \"#<buf>\" refers to the buffer \"buf\"."
+ (with-temp-buffer
+ (rename-buffer "my-buffer" t)
+ (eshell-command-result-equal
+ (format "echo #<%s>" (buffer-name))
+ (current-buffer))))
+
+(ert-deftest esh-arg-test/special-reference/buffer ()
+ "Test that \"#<buffer buf>\" refers to the buffer \"buf\"."
+ (with-temp-buffer
+ (rename-buffer "my-buffer" t)
+ (eshell-command-result-equal
+ (format "echo #<buffer %s>" (buffer-name))
+ (current-buffer))))
+
+(ert-deftest esh-arg-test/special-reference/special ()
+ "Test that \"#<...>\" works correctly when escaping special characters."
+ (with-temp-buffer
+ (rename-buffer "<my buffer>" t)
+ (let ((escaped-bufname (replace-regexp-in-string
+ (rx (group (or "\\" "<" ">" space))) "\\\\\\1"
+ (buffer-name))))
+ (eshell-command-result-equal
+ (format "echo #<%s>" escaped-bufname)
+ (current-buffer))
+ (eshell-command-result-equal
+ (format "echo #<buffer %s>" escaped-bufname)
+ (current-buffer)))))
+
;; esh-arg-tests.el ends here
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* bug#53371: 28.0.90; eshell completion error when trying to complete buffer name, args-out-of-range
2023-02-24 17:53 ` Jim Porter
@ 2023-03-12 3:35 ` Jim Porter
0 siblings, 0 replies; 6+ messages in thread
From: Jim Porter @ 2023-03-12 3:35 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 53371-done, mail
On 2/24/2023 9:53 AM, Jim Porter wrote:
> On 2/24/2023 7:09 AM, Stefan Monnier via Bug reports for GNU Emacs, the
> Swiss army knife of text editors wrote:
>>> + (let ((buffer-p (if (match-beginning 1)
>>> + (equal (match-string 1) "buffer")
>>> t)) ;; buffer-p is non-nil by default.
>>
>> The comment just describes what the code does but it would be more
>> useful to explain why.
>
> Thanks, fixed.
Since there haven't been any further comments, merged as 9199fa00caa.
Closing this bug now, though if there are any remaining issues, just let
me know.
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2023-03-12 3:35 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-19 17:35 bug#53371: 28.0.90; eshell completion error when trying to complete buffer name, args-out-of-range Daniel Mendler
2023-02-02 6:40 ` Jim Porter
2023-02-24 6:40 ` Jim Porter
2023-02-24 15:09 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-02-24 17:53 ` Jim Porter
2023-03-12 3:35 ` Jim Porter
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).