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.
This commit is contained in:
parent
b8e7061232
commit
9199fa00ca
4 changed files with 135 additions and 13 deletions
|
|
@ -317,7 +317,7 @@ to writing a completion function."
|
|||
(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 @@ to writing a completion function."
|
|||
(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 @@ to writing a completion function."
|
|||
(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))))
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@
|
|||
;;; Code:
|
||||
|
||||
(require 'esh-util)
|
||||
(require 'esh-module)
|
||||
|
||||
(require 'pcomplete)
|
||||
|
||||
(eval-when-compile
|
||||
(require 'cl-lib))
|
||||
|
|
@ -175,7 +178,11 @@ treated as a literal character."
|
|||
"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 @@ If the form has no `type', the syntax is parsed as if `type' were
|
|||
\"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 @@ If no argument requested a splice, return nil."
|
|||
(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
|
||||
|
|
|
|||
|
|
@ -176,6 +176,46 @@ See <lisp/eshell/esh-cmd.el>."
|
|||
(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>."
|
||||
|
|
|
|||
|
|
@ -102,4 +102,34 @@ treated literally, as a backslash and a 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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue