[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
feature/android dc7ecc6e31a: Merge remote-tracking branch 'origin/master
From: |
Po Lu |
Subject: |
feature/android dc7ecc6e31a: Merge remote-tracking branch 'origin/master' into feature/android |
Date: |
Mon, 10 Jul 2023 20:26:19 -0400 (EDT) |
branch: feature/android
commit dc7ecc6e31ae431e060464a9a24f050cc29077a3
Merge: cf2dde4261a 3b7273f4ae3
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>
Merge remote-tracking branch 'origin/master' into feature/android
---
configure.ac | 7 +++--
doc/lispref/files.texi | 6 +++-
doc/misc/eshell.texi | 43 ++++++++++++++++++++++++++-
etc/NEWS | 9 ++++++
lib/gnulib.mk.in | 1 -
lisp/eshell/esh-ext.el | 62 +++++++++++++++++++++++++--------------
lisp/files.el | 4 ++-
lisp/net/tramp.el | 5 ++--
lisp/progmodes/eglot.el | 35 +++++++++++++++-------
lisp/progmodes/project.el | 3 +-
lisp/subr.el | 2 +-
test/lisp/eshell/esh-ext-tests.el | 32 ++++++++++++++++++++
test/lisp/net/tramp-tests.el | 4 ++-
13 files changed, 168 insertions(+), 45 deletions(-)
diff --git a/configure.ac b/configure.ac
index 35409471608..48d474f6888 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1545,9 +1545,9 @@ AC_DEFUN([gt_TYPE_WINT_T],
[GNULIBHEADERS_OVERRIDE_WINT_T=0
AC_SUBST([GNULIBHEADERS_OVERRIDE_WINT_T])])
# Emacs does not need precise checks for the Solaris 10 MB_CUR_MAX bug.
-AC_DEFUN([gt_LOCALE_FR_UTF8],
- [LOCALE_FR_UTF8=none
- AC_SUBST([LOCALE_FR_UTF8])])
+AC_DEFUN_ONCE([gl_STDLIB_H],
+ [AC_REQUIRE([gl_STDLIB_H_DEFAULTS])
+ gl_NEXT_HEADERS([stdlib.h])])
# Initialize gnulib right after choosing the compiler.
dnl Amongst other things, this sets AR and ARFLAGS.
@@ -7191,6 +7191,7 @@ if test "$emacs_cv_struct_alignment" = yes; then
structure to an N-byte boundary.])
fi
+AC_C_RESTRICT
AC_C_TYPEOF
AC_CACHE_CHECK([for statement expressions],
diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi
index 66de0f036c4..31d4aaca507 100644
--- a/doc/lispref/files.texi
+++ b/doc/lispref/files.texi
@@ -3630,7 +3630,11 @@ be @code{root}.
If @var{connected} is non-@code{nil}, this function returns @code{nil}
even if @var{filename} is remote, if Emacs has no network connection
to its host. This is useful when you want to avoid the delay of
-making connections when they don't exist.
+making connections when they don't exist. If @var{connected} is
+@code{never}, @emph{never} use an existing connection to return the
+identification, even if one is already present (this is otherwise like
+a value of @code{nil}). This lets you prevent any connection-specific
+logic, such as expanding the local part of the file name.
@end defun
@defun unhandled-file-name-directory filename
diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi
index c6376882542..ecc12035650 100644
--- a/doc/misc/eshell.texi
+++ b/doc/misc/eshell.texi
@@ -207,6 +207,7 @@ that will be invoked, type this as the Eshell prompt:
* Built-ins::
* Variables::
* Aliases::
+* Remote Access::
* History::
* Completion::
* Control Flow::
@@ -266,7 +267,10 @@ As an ordinary Lisp function
@vindex eshell-prefer-lisp-functions
If you would prefer to use ordinary Lisp functions over external
programs, set the option @code{eshell-prefer-lisp-functions} to
-@code{t}. This will swap the lookup order of the last two items.
+@code{t}. This will swap the lookup order of the last two items. You
+can also force Eshell to look for a command as an external program by
+prefixing its name with @kbd{*}, like @code{*@var{command}}
+(@pxref{Built-ins}).
You can also group command forms together into a subcommand with curly
braces (@code{@{@}}). This lets you use the output of a subcommand as
@@ -1090,6 +1094,43 @@ create and switch to a directory called @samp{foo}.
@end table
+@node Remote Access
+@section Remote Access
+@cmindex remote access
+
+Since Eshell uses Emacs facilities for most of its functionality, you
+can access remote hosts transparently. To connect to a remote host,
+simply @code{cd} into it:
+
+@example
+~ $ cd /ssh:user@@remote:
+/ssh:user@@remote:~ $
+@end example
+
+Additionally, built-in Eshell commands (@pxref{Built-ins}) and
+ordinary Lisp functions accept remote file names, so you can access
+them even without explicitly connecting first. For example, to print
+the contents of a remote file, you could type @samp{cat
+/ssh:user@@remote:~/output.log}. However, this means that when using
+built-in commands or Lisp functions from a remote directory, you must
+be careful about specifying absolute file names: @samp{cat
+/var/output.log} will always print the contents of your @emph{local}
+@file{/var/output.log}, even from a remote directory. If you find
+this behavior annoying, you can enable the optional electric forward
+slash module (@pxref{Electric forward slash}).
+
+@vindex eshell-explicit-remote-commands
+When running commands, you can also make them explicitly remote by
+prefixing the command name with a remote identifier, e.g.@:
+@samp{/ssh:user@@remote:whoami}. This runs the command @code{whoami}
+over the SSH connection for @code{user@@remote}, no matter your
+current directory. If you want to explicitly run a @emph{local}
+command even when in a remote directory, you can prefix the command
+name with @kbd{/:}, like @samp{/:whoami}. In either case, you can
+also specify the absolute path to the program, e.g.@:
+@samp{/ssh:user@@remote:/usr/bin/whoami}. To disable this syntax, set
+the option @code{eshell-explicit-remote-commands} to @code{nil}.
+
@node History
@section History
@cmindex history
diff --git a/etc/NEWS b/etc/NEWS
index 304505e50da..db4fa9f403e 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -224,6 +224,15 @@ or get a sublist of elements 2 through 4 with
'$my-list[2..5]'. For
more information, see the "(eshell) Dollars Expansion" node in the
Eshell manual.
++++
+*** Eshell commands can now be explicitly-remote (or local).
+By prefixing a command name in Eshell with a remote identifier, like
+"/ssh:user@remote:whoami", you can now runs commands on a particular
+host no matter your current directory. Likewise, you can run a
+command on your local system no matter your current directory via
+"/:whoami". For more information, see the "(eshell) Remote Access"
+node in the Eshell manual.
+
+++
*** Eshell's '$UID' and '$GID' variables are now connection-aware.
Now, when expanding '$UID' or '$GID' in a remote directory, the value
diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in
index 893ac789162..4fef3c4c82d 100644
--- a/lib/gnulib.mk.in
+++ b/lib/gnulib.mk.in
@@ -1197,7 +1197,6 @@ LIB_WSOCK32 = @LIB_WSOCK32@
LIB_XATTR = @LIB_XATTR@
LIMITS_H = @LIMITS_H@
LN_S_FILEONLY = @LN_S_FILEONLY@
-LOCALE_FR_UTF8 = @LOCALE_FR_UTF8@
LTLIBGMP = @LTLIBGMP@
LTLIBINTL = @LTLIBINTL@
LTLIBOBJS = @LTLIBOBJS@
diff --git a/lisp/eshell/esh-ext.el b/lisp/eshell/esh-ext.el
index f350622e78c..38579d7ef1c 100644
--- a/lisp/eshell/esh-ext.el
+++ b/lisp/eshell/esh-ext.el
@@ -168,11 +168,23 @@ external version."
:type 'character
:group 'eshell-ext)
+(defcustom eshell-explicit-remote-commands t
+ "If non-nil, support explicitly-remote commands.
+These are commands with a full remote file name, such as
+\"/ssh:host:whoami\". If this is enabled, you can also run
+explicitly-local commands by using a quoted file name, like
+\"/:whoami\"."
+ :type 'boolean
+ :group 'eshell-ext)
+
;;; Functions:
(defun eshell-ext-initialize () ;Called from `eshell-mode' via intern-soft!
"Initialize the external command handling code."
- (add-hook 'eshell-named-command-hook #'eshell-explicit-command nil t))
+ (add-hook 'eshell-named-command-hook #'eshell-explicit-command nil t)
+ (when eshell-explicit-remote-commands
+ (add-hook 'eshell-named-command-hook
+ #'eshell-handle-remote-command nil t)))
(defun eshell-explicit-command (command args)
"If a command name begins with `*', call it externally always.
@@ -186,30 +198,36 @@ This bypasses all Lisp functions and aliases."
(error "%s: external command not found"
(substring command 1))))))
+(defun eshell-handle-remote-command (command args)
+ "Handle remote (or quoted) COMMAND names, using ARGS.
+This calls the appropriate function for commands that aren't on
+the connection associated with `default-directory'. (See
+`eshell-explicit-remote-commands'.)"
+ (if (file-name-quoted-p command)
+ (let ((default-directory (if (file-remote-p default-directory)
+ (expand-file-name "~")
+ default-directory)))
+ (eshell-external-command (file-name-unquote command) args))
+ (when (file-remote-p command)
+ (eshell-remote-command command args))))
+
(defun eshell-remote-command (command args)
"Insert output from a remote COMMAND, using ARGS.
A remote command is something that executes on a different machine.
-An external command simply means external to Emacs.
-
-Note that this function is very crude at the moment. It gathers up
-all the output from the remote command, and sends it all at once,
-causing the user to wonder if anything's really going on..."
- (let ((outbuf (generate-new-buffer " *eshell remote output*"))
- (errbuf (generate-new-buffer " *eshell remote error*"))
- (command (file-local-name command))
- (exitcode 1))
- (unwind-protect
- (progn
- (setq exitcode
- (shell-command
- (mapconcat #'shell-quote-argument
- (append (list command) args) " ")
- outbuf errbuf))
- (eshell-print (with-current-buffer outbuf (buffer-string)))
- (eshell-error (with-current-buffer errbuf (buffer-string))))
- (eshell-close-handles exitcode 'nil)
- (kill-buffer outbuf)
- (kill-buffer errbuf))))
+An external command simply means external to Emacs."
+ (let* ((cwd-connection (file-remote-p default-directory))
+ (command-connection (file-remote-p command))
+ (default-directory (if (equal cwd-connection command-connection)
+ default-directory
+ command-connection))
+ ;; Never use the remote connection here. We don't want to
+ ;; expand the local name! Instead, we want it as the user
+ ;; typed, so that if COMMAND is "/ssh:host:cat", we just get
+ ;; "cat" as the result.
+ (command-localname (file-remote-p command 'localname 'never)))
+ (unless command-connection
+ (error "%s: not a remote command" command))
+ (eshell-external-command command-localname args)))
(defun eshell-external-command (command args)
"Insert output from an external COMMAND, using ARGS."
diff --git a/lisp/files.el b/lisp/files.el
index 7cbda41b6c0..95195f51503 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -1270,7 +1270,9 @@ there is an existing connection.
If CONNECTED is non-nil, return an identification only if FILE is
located on a remote system and a connection is established to
-that remote system.
+that remote system. If CONNECTED is `never', never use an
+existing connection to return the identification (this is
+otherwise like a value of nil).
Tip: You can use this expansion of remote identifier components
to derive a new remote file name from an existing one. For
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index ab868153236..be6178ff707 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -4341,13 +4341,14 @@ Let-bind it when necessary.")
(let ((tramp-verbose (min tramp-verbose 3)))
(when (tramp-tramp-file-p filename)
(let* ((o (tramp-dissect-file-name filename))
- (p (tramp-get-connection-process o))
+ (p (and (not (eq connected 'never))
+ (tramp-get-connection-process o)))
(c (and (process-live-p p)
(tramp-get-connection-property p "connected"))))
;; We expand the file name only, if there is already a connection.
(with-parsed-tramp-file-name
(if c (expand-file-name filename) filename) nil
- (and (or (not connected) c)
+ (and (or (memq connected '(nil never)) c)
(cond
((eq identification 'method) method)
;; Domain and port are appended to user and host,
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index f09c348143d..0c46e4a5290 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -463,7 +463,7 @@ This can be useful when using docker to run a language
server.")
(eval-and-compile
(defvar eglot--lsp-interface-alist
`(
- (CodeAction (:title) (:kind :diagnostics :edit :command :isPreferred))
+ (CodeAction (:title) (:kind :diagnostics :edit :command :isPreferred
:data))
(ConfigurationItem () (:scopeUri :section))
(Command ((:title . string) (:command . string)) (:arguments))
(CompletionItem (:label)
@@ -739,9 +739,12 @@ ACTION is an LSP object of either `CodeAction' or
`Command' type."
(server action) "Default implementation."
(eglot--dcase action
(((Command)) (eglot--request server :workspace/executeCommand action))
- (((CodeAction) edit command)
- (when edit (eglot--apply-workspace-edit edit))
- (when command (eglot--request server :workspace/executeCommand
command))))))
+ (((CodeAction) edit command data)
+ (if (and (null edit) (null command) data
+ (eglot--server-capable :codeActionProvider :resolveProvider))
+ (eglot-execute server (eglot--request server :codeAction/resolve
action))
+ (when edit (eglot--apply-workspace-edit edit))
+ (when command (eglot--request server :workspace/executeCommand
command)))))))
(cl-defgeneric eglot-initialization-options (server)
"JSON object to send under `initializationOptions'."
@@ -825,6 +828,7 @@ ACTION is an LSP object of either `CodeAction' or `Command'
type."
:documentHighlight `(:dynamicRegistration :json-false)
:codeAction (list
:dynamicRegistration :json-false
+ :resolveSupport t :dataSupport t
:codeActionLiteralSupport
'(:codeActionKind
(:valueSet
@@ -3197,11 +3201,25 @@ for which LSP on-type-formatting should be requested."
sig
(with-temp-buffer
(insert siglabel)
- ;; Ad-hoc attempt to parse label as <name>(<params>)
;; Add documentation, indented so we can distinguish multiple signatures
(when-let (doc (and (not briefp) sigdoc (eglot--format-markup sigdoc)))
(goto-char (point-max))
(insert "\n" (replace-regexp-in-string "^" " " doc)))
+ ;; Try to highlight function name only
+ (let (first-parlabel)
+ (cond ((and (cl-plusp (length parameters))
+ (vectorp (setq first-parlabel
+ (plist-get (aref parameters 0) :label))))
+ (save-excursion
+ (goto-char (elt first-parlabel 0))
+ (skip-syntax-backward "^w")
+ (add-face-text-property (point-min) (point)
+ 'font-lock-function-name-face)))
+ ((save-excursion
+ (goto-char (point-min))
+ (looking-at "\\([^(]*\\)([^)]*)"))
+ (add-face-text-property (match-beginning 1) (match-end 1)
+ 'font-lock-function-name-face))))
;; Now to the parameters
(cl-loop
with active-param = (or sig-active activeParameter)
@@ -3210,13 +3228,8 @@ for which LSP on-type-formatting should be requested."
((:label parlabel))
((:documentation pardoc)))
parameter
- (when (zerop i)
- (goto-char (elt parlabel 0))
- (skip-syntax-backward "^w")
- (add-face-text-property (point-min) (point)
- 'font-lock-function-name-face))
;; ...perhaps highlight it in the formals list
- (when (= i active-param)
+ (when (eq i active-param)
(save-excursion
(goto-char (point-min))
(pcase-let
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index 56c524bcab5..1d5a5fa5c63 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -1679,7 +1679,8 @@ It's also possible to enter an arbitrary directory not in
the list."
ret))
;; XXX: Just using this for the category (for the substring
;; completion style).
- (table (project--file-completion-table (cons dir-choice choices)))
+ (table (project--file-completion-table
+ (reverse (cons dir-choice choices))))
(pr-name ""))
(while (equal pr-name "")
;; If the user simply pressed RET, do this again until they don't.
diff --git a/lisp/subr.el b/lisp/subr.el
index 0a91c16c787..2d52ecf201f 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -5056,7 +5056,7 @@ but that should be robust in the unexpected case that an
error is signaled."
;; The use without `format' is obsolete, let's warn when we bump
;; into any such remaining uses.
(macroexp-warn-and-return
- (format-message "Missing format argument in `with-demote-errors'")
+ (format-message "Missing format argument in `with-demoted-errors'")
exp nil nil
orig-format))))
diff --git a/test/lisp/eshell/esh-ext-tests.el
b/test/lisp/eshell/esh-ext-tests.el
index ef073d3487d..aae297cd413 100644
--- a/test/lisp/eshell/esh-ext-tests.el
+++ b/test/lisp/eshell/esh-ext-tests.el
@@ -23,6 +23,7 @@
;;; Code:
+(require 'tramp)
(require 'ert)
(require 'esh-mode)
(require 'esh-ext)
@@ -73,4 +74,35 @@
(eshell-match-command-output "echo $PATH"
(concat original-path "\n")))))
+(ert-deftest esh-ext-test/explicitly-remote-command ()
+ "Test that an explicitly-remote command is remote no matter the current dir."
+ (skip-unless (and (eshell-tests-remote-accessible-p)
+ (executable-find "sh")))
+ (dolist (default-directory (list default-directory
+ ert-remote-temporary-file-directory))
+ (dolist (cmd (list "sh" (executable-find "sh")))
+ (ert-info ((format "Directory: %s; executable: %s" default-directory
cmd))
+ (with-temp-eshell
+ ;; Check the value of $INSIDE_EMACS using `sh' in order to
+ ;; delay variable expansion.
+ (eshell-match-command-output
+ (format "%s%s -c 'echo $INSIDE_EMACS'"
+ (file-remote-p ert-remote-temporary-file-directory) cmd)
+ "eshell,tramp"))))))
+
+(ert-deftest esh-ext-test/explicitly-local-command ()
+ "Test that an explicitly-local command is local no matter the current dir."
+ (skip-unless (and (eshell-tests-remote-accessible-p)
+ (executable-find "sh")))
+ (dolist (default-directory (list default-directory
+ ert-remote-temporary-file-directory))
+ (dolist (cmd (list "sh" (executable-find "sh")))
+ (ert-info ((format "In directory: %s" default-directory))
+ (with-temp-eshell
+ ;; Check the value of $INSIDE_EMACS using `sh' in order to
+ ;; delay variable expansion.
+ (eshell-match-command-output
+ (format "/:%s -c 'echo $INSIDE_EMACS'" cmd)
+ "eshell\n"))))))
+
;; esh-ext-tests.el ends here
diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el
index 51980e335e7..16afc0f477a 100644
--- a/test/lisp/net/tramp-tests.el
+++ b/test/lisp/net/tramp-tests.el
@@ -6073,7 +6073,9 @@ INPUT, if non-nil, is a string sent to the process."
;; Unset the variable.
(let ((tramp-remote-process-environment
(cons (concat envvar "=foo") tramp-remote-process-environment)))
- ;; Set the initial value, we want to unset below.
+ ;; Refill the cache; we don't want to run into timeouts.
+ (file-truename default-directory)
+ ;; Check the initial value, we want to unset below.
(should
(string-match-p
"foo"