* 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 external index https://git.savannah.gnu.org/cgit/emacs.git https://git.savannah.gnu.org/cgit/emacs/org-mode.git This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.