Compare commits

...

50 commits

Author SHA1 Message Date
Michael Albinus
5a22f5ac9a ; Whitespace cleanup 2023-03-04 10:19:38 +01:00
Michael Albinus
b14cd8380e Merge branch 'master' into feature/tramp-thread-safe 2023-03-04 10:17:25 +01:00
Michael Albinus
a4972d96f9 Merge remote-tracking branch 'origin/master' into feature/tramp-thread-safe 2020-03-27 11:05:00 +01:00
Michael Albinus
47156f117b Merge remote-tracking branch 'origin/master' into feature/tramp-thread-safe 2020-02-17 09:56:44 +01:00
Michael Albinus
ac98caa0cd Sync with Tramp 2.5.0 2020-02-17 09:54:42 +01:00
Michael Albinus
47278d3724 Merge remote-tracking branch 'origin/master' into feature/tramp-thread-safe 2020-02-15 17:04:45 +01:00
Michael Albinus
90ae1ae776 * lisp/net/tramp-cache.el (tramp-cache-print): Handle mutexes. 2018-11-10 15:47:08 +01:00
Michael Albinus
ec175a4a49 * lisp/net/tramp-ftp.el (ange-ftp-ftp-process-buffer): Move up. 2018-11-08 12:03:28 +01:00
Michael Albinus
c7e311e5ba * lisp/net/tramp-ftp.el (ange-ftp-ftp-process-buffer): Declare. 2018-11-06 15:50:30 +01:00
Michael Albinus
1488024713 Merge remote-tracking branch 'origin/master' into feature/tramp-thread-safe 2018-11-05 14:40:25 +01:00
Michael Albinus
80da4fa750 Fix Bug#33135
* lisp/net/tramp-ftp.el (tramp-ftp-file-name-handler): Support threads.
(Bug#33135)
2018-11-05 14:31:07 +01:00
Michael Albinus
25f77f9085 Merge remote-tracking branch 'origin/master' into feature/tramp-thread-safe 2018-10-12 15:09:03 +02:00
Michael Albinus
c8ca97af38 Merge remote-tracking branch 'origin/master' into feature/tramp-thread-safe 2018-09-29 15:45:53 +02:00
Michael Albinus
853ade4a65 Merge remote-tracking branch 'origin/master' into feature/tramp-thread-safe 2018-09-10 14:36:01 +02:00
Michael Albinus
89b80ad4bc Remove thread-list.el from a previous merge 2018-09-10 13:21:22 +02:00
Michael Albinus
61be02000e * src/window.c (run_window_size_change_functions): Fix typo. 2018-09-10 13:17:29 +02:00
Michael Albinus
c291336562 Merge remote-tracking branch 'origin/master' into feature/tramp-thread-safe 2018-09-10 13:13:36 +02:00
Michael Albinus
bedde1fc7a Fix Bug#32537
* lisp/net/tramp-sh.el (tramp-sh-handle-file-attributes)
(tramp-sh-handle-file-newer-than-file-p)
(tramp-sh-handle-directory-files-and-attributes)
(tramp-sh-handle-make-directory)
(tramp-sh-handle-dired-compress-file)
(tramp-sh-handle-file-local-copy): Get rid of `save-excursion'.
(tramp-convert-file-attributes): Use `save-match-data'.  (Bug#32537)
2018-09-02 10:25:02 +02:00
Michael Albinus
0361de2f5f * test/lisp/net/tramp-tests.el (tramp-test43-threads): Use `thread-live-p'. 2018-08-31 13:23:33 +02:00
Michael Albinus
1ed8d0030b Merge branch 'master' into feature/tramp-thread-safe 2018-08-31 13:10:21 +02:00
Michael Albinus
279618b685 Do not propagate Tramp errors to the main thread
* lisp/files.el (find-file-noselect): Use `with-demoted-errors'
for the joined threads.

* lisp/net/tramp-compat.el (tramp-compat-signal): Remove.

* lisp/net/tramp.el (tramp-error, tramp-file-name-handler): Do not
propagate the error to the main thread.
2018-08-30 14:30:49 +02:00
Michael Albinus
7e6b0b33c0 Merge lisp/net/trampver.el 2018-08-29 10:57:17 +02:00
Michael Albinus
06a2f822a2 Merge branch 'master' into feature/tramp-thread-safe 2018-08-29 10:56:02 +02:00
Michael Albinus
a692095fd8 Document prefix commands
* doc/lispref/commands.texi (Prefix Commands): New node.
(Command Loop):
* doc/lispref/elisp.texi (Top): Add Prefix Commands menue entry.
2018-08-25 13:10:10 +02:00
Michael Albinus
162353c45c Change Tramp version to 2.4.1-pre
* doc/misc/trampver.texi:
* lisp/net/trampver.el: Change version to "2.4.1-pre".
2018-08-17 14:35:47 +02:00
Michael Albinus
9432b70f76 * lisp/files.el (execute-file-commands-asynchronously): Fix docstring. 2018-08-17 10:36:04 +02:00
Michael Albinus
2ff7017956 Merge branch 'master' into feature/tramp-thread-safe 2018-08-17 10:33:10 +02:00
Michael Albinus
5df7423b4a Fix Bug#32447
* lisp/files.el (find-file-existing, find-file--read-only):
Use `find-file-with-threads'.  (Bug#32447)
2018-08-16 10:04:11 +02:00
Michael Albinus
43cac37e08 * lisp/net/tramp.el (tramp-debug-message):
`tramp-compat-main-thread' could be nil.
2018-08-09 15:55:54 +02:00
Michael Albinus
8068f14b5f * lisp/simple.el (universal-async-argument): Fix docstring. 2018-08-09 15:55:40 +02:00
Michael Albinus
646ad72871 ; * doc/emacs/files.texi (Visiting): Improve wording. 2018-08-07 18:14:05 +02:00
Michael Albinus
8f3b6e962d Minor edits for async file visiting
* doc/emacs/files.texi (Visiting): Fix typo.

* etc/NEWS (execute-file-commands-asynchronously): Be more verbose
about possible values.

* lisp/simple.el (universal-async-argument): Handle also
`negative-argument'.
2018-08-07 16:20:23 +02:00
Michael Albinus
c8b5b16eec Merge branch 'master' into feature/tramp-thread-safe 2018-08-07 12:36:13 +02:00
Michael Albinus
ef57169b99 Update documentation wrt universal-async-argument
* doc/emacs/files.texi (Visiting): Add @kindex.  Mention command name.

* doc/lispref/files.texi (Visiting Functions): Adapt wording.

* lisp/files.el (find-file, find-file-other-window)
(find-file-other-frame, find-file-existing)
(find-alternate-file-other-window, find-alternate-file)
(execute-file-commands-asynchronously, find-file-literally):
Adapt docstring.
2018-08-07 12:32:04 +02:00
Michael Albinus
b823346b65 Generalize universal-async-argument
* doc/lispref/files.texi (Visiting Functions):
Mention universal-async-argument.  Add @kindex and @findex.

* etc/NEWS: Mention 'universal-async-argument'.

* lisp/files.el (universal-async-argument): Move it to simple.el.
(find-file-read-args, find-alternate-file-other-window)
(find-alternate-file): Check for `universal-async-argument' variable.

* lisp/simple.el (universal-async-argument): New variable.
(universal-async-argument): Move function from files.el.  Don't
use an argument.  Toggle `universal-async-argument' variable.
2018-08-06 12:26:43 +02:00
Michael Albinus
8db0de97ac Merge branch 'master' into feature/tramp-thread-safe 2018-08-05 19:53:42 +02:00
Michael Albinus
33363e5f62 Rework "async" toggling of file visiting commands
* doc/emacs/files.texi (Visiting):
* doc/lispref/files.texi (Visiting Functions):
* etc/NEWS: Use 'execute-file-commands-asynchronously' rather than
'find-file-asynchronously'.

* lisp/files.el (universal-async-argument): New defun.  Bind it
to "C-x &".
(find-file, find-file-other-window, find-file-other-frame)
(find-file-existing, find-alternate-file-other-window)
(find-alternate-file, find-file-literally): Adapt docstring.
(execute-file-commands-asynchronously): Rename from
`find-file-asynchronously'.
(find-file-read-args, find-alternate-file-other-window)
(find-alternate-file): Use it.
2018-08-05 11:45:22 +02:00
Michael Albinus
d1f39ebc91 Merge branch 'feature/tramp-thread-safe' 2018-08-04 12:34:04 +02:00
Michael Albinus
d7f3187c2e Improve vc-mutex use
* lisp/files.el (find-file-with-threads): Don't lock `vc-mutex'
here ...
(find-file-noselect): ... but here when wildcards are enabled.

* lisp/vc/vc-hooks.el (vc-refresh-state): Lock `vc-mutex' only at
thread start.  Use buffer name as thread name.
2018-08-04 12:31:58 +02:00
Gemini Lasswell
11ff7fa18c Add list-threads command and thread-list-mode
* lisp/emacs-lisp/thread-list.el: New file.
2018-08-04 12:31:58 +02:00
Michael Albinus
4615b9148c Document visiting files asynchronously
* doc/emacs/files.texi (Visiting):
* doc/lispref/files.texi (Visiting Functions): Document visiting
files asynchronously.

* etc/NEWS: Rework entry about asynchronous file visiting.

* lisp/files.el (find-file, find-file-other-window)
(find-file-other-frame, find-file-existing)
(find-alternate-file-other-window, find-alternate-file)
(find-file-literally): Adapt docstring.
2018-08-04 12:31:58 +02:00
Michael Albinus
d7314b35ff find-file-asynchronously can also be a regexp
* lisp/files.el (find-file-read-args): Check, whether
find-file-asynchronously matches filename, if the former is a regexp.
(find-file-asynchronously): Allow also regexp.
2018-08-04 12:31:58 +02:00
Michael Albinus
4d340cd956 Add find-file-asynchronously
* lisp/array.el (xor): Move function ...
* lisp/subr.el (xor): ... here.

* lisp/files.el (find-file-asynchronously): New defcustom.
(find-file-read-args): Use it.
(find-alternate-file-other-window, find-alternate-file): Use it.
Rename arg to ASYNC.
(find-file-with-threads, find-file, find-file-other-window)
(find-file-other-frame, find-file-existing, find-file--read-only)
(find-file-read-only, find-file-read-only-other-window)
(find-file-read-only-other-frame)
(find-alternate-file-other-window, find-file-noselect)
(find-file-literally): Rename arg to ASYNC.
2018-08-04 12:31:57 +02:00
Michael Albinus
bba62df633 Introducing thread-safe Tramp
* doc/misc/tramp.texi (History): Tramp is thread-safe now.

* etc/NEWS: Mention asynchronous visiting of files, and Tramp being
thread-safe.

* lisp/files.el (find-file-read-args): Add optional arument WILDCARDS.
(find-file-with-threads): New defmacro.
(find-file, find-file-other-window, find-file-other-frame)
(find-file-existing, find-alternate-file-other-window)
(find-alternate-file, find-file-literally): New optional argument
THREADS.  Adapt docstring if necessary.  Wrap the body by
`find-file-with-threads'.
(find-file--read-only, find-file-read-only)
(find-file-read-only-other-window)
(find-file-read-only-other-frame): New optional argument THREADS.
(find-file-noselect): New optional argument THREADS.  Adapt
docstring.  Create new threads for all files detected through WILDCARDS.

* lisp/net/tramp-compat.el (tramp-compat-main-thread): New defconst.
(tramp-compat-current-thread, tramp-compat-thread-yield)
(tramp-compat-signal): New defsubst.
(tramp-compat-with-mutex): New defmacro.

* lisp/net/tramp.el (tramp-debug-outline-regexp): Add thread regexp.
(tramp-debug-font-lock-keywords): New defconst.
(tramp-debug-outline-level): Adapt to changed
`tramp-debug-outline-regexp'.
(tramp-get-debug-buffer): Use `tramp-debug-font-lock-keywords'.
(tramp-debug-message): Insert also thread name.
(tramp-error): Propagate error to main thread.
(tramp-condition-case-unless-debug): Add declarations.
(tramp-mutex): New defvar.
(tramp-get-mutex): New defun.
(tramp-file-name-handler): Adapt to threads.  Lock Tramp mutex.
Set process thread.  Propagate signals to main thread.

* lisp/vc/vc-hooks.el (vc-mutex): New autoloaded variable.
(vc-refresh-state): Let it run in own thread.  Lock it with `vc-mutex'.

* test/lisp/net/tramp-archive-tests.el (tramp-archive-test44-auto-load)
(tramp-archive-test44-delay-load): Rename.

* test/lisp/net/tramp-tests.el (tramp-test43-threads): New test.
(tramp-test44-auto-load, tramp-test44-delay-load)
(tramp-test44-recursive-load, tramp-test44-remote-load-path)
(tramp-test45-unload): Rename.
2018-08-04 12:31:57 +02:00
Michael Albinus
c6db5347c5 Improve vc-mutex use
* lisp/files.el (find-file-with-threads): Don't lock `vc-mutex'
here ...
(find-file-noselect): ... but here when wildcards are enabled.

* lisp/vc/vc-hooks.el (vc-refresh-state): Lock `vc-mutex' only at
thread start.  Use buffer name as thread name.
2018-07-29 11:59:21 +02:00
Gemini Lasswell
0de175d231 Add list-threads command and thread-list-mode
* lisp/emacs-lisp/thread-list.el: New file.
2018-07-29 09:07:00 +02:00
Michael Albinus
18c46ed2dd Document visiting files asynchronously
* doc/emacs/files.texi (Visiting):
* doc/lispref/files.texi (Visiting Functions): Document visiting
files asynchronously.

* etc/NEWS: Rework entry about asynchronous file visiting.

* lisp/files.el (find-file, find-file-other-window)
(find-file-other-frame, find-file-existing)
(find-alternate-file-other-window, find-alternate-file)
(find-file-literally): Adapt docstring.
2018-07-28 17:30:26 +02:00
Michael Albinus
917a542d72 find-file-asynchronously can also be a regexp
* lisp/files.el (find-file-read-args): Check, whether
find-file-asynchronously matches filename, if the former is a regexp.
(find-file-asynchronously): Allow also regexp.
2018-07-28 10:40:37 +02:00
Michael Albinus
80acd88e50 Add find-file-asynchronously
* lisp/array.el (xor): Move function ...
* lisp/subr.el (xor): ... here.

* lisp/files.el (find-file-asynchronously): New defcustom.
(find-file-read-args): Use it.
(find-alternate-file-other-window, find-alternate-file): Use it.
Rename arg to ASYNC.
(find-file-with-threads, find-file, find-file-other-window)
(find-file-other-frame, find-file-existing, find-file--read-only)
(find-file-read-only, find-file-read-only-other-window)
(find-file-read-only-other-frame)
(find-alternate-file-other-window, find-file-noselect)
(find-file-literally): Rename arg to ASYNC.
2018-07-27 16:59:09 +02:00
Michael Albinus
cf4e1b18aa Introducing thread-safe Tramp
* doc/misc/tramp.texi (History): Tramp is thread-safe now.

* etc/NEWS: Mention asynchronous visiting of files, and Tramp being
thread-safe.

* lisp/files.el (find-file-read-args): Add optional arument WILDCARDS.
(find-file-with-threads): New defmacro.
(find-file, find-file-other-window, find-file-other-frame)
(find-file-existing, find-alternate-file-other-window)
(find-alternate-file, find-file-literally): New optional argument
THREADS.  Adapt docstring if necessary.  Wrap the body by
`find-file-with-threads'.
(find-file--read-only, find-file-read-only)
(find-file-read-only-other-window)
(find-file-read-only-other-frame): New optional argument THREADS.
(find-file-noselect): New optional argument THREADS.  Adapt
docstring.  Create new threads for all files detected through WILDCARDS.

* lisp/net/tramp-compat.el (tramp-compat-main-thread): New defconst.
(tramp-compat-current-thread, tramp-compat-thread-yield)
(tramp-compat-signal): New defsubst.
(tramp-compat-with-mutex): New defmacro.

* lisp/net/tramp.el (tramp-debug-outline-regexp): Add thread regexp.
(tramp-debug-font-lock-keywords): New defconst.
(tramp-debug-outline-level): Adapt to changed
`tramp-debug-outline-regexp'.
(tramp-get-debug-buffer): Use `tramp-debug-font-lock-keywords'.
(tramp-debug-message): Insert also thread name.
(tramp-error): Propagate error to main thread.
(tramp-condition-case-unless-debug): Add declarations.
(tramp-mutex): New defvar.
(tramp-get-mutex): New defun.
(tramp-file-name-handler): Adapt to threads.  Lock Tramp mutex.
Set process thread.  Propagate signals to main thread.

* lisp/vc/vc-hooks.el (vc-mutex): New autoloaded variable.
(vc-refresh-state): Let it run in own thread.  Lock it with `vc-mutex'.

* test/lisp/net/tramp-archive-tests.el (tramp-archive-test44-auto-load)
(tramp-archive-test44-delay-load): Rename.

* test/lisp/net/tramp-tests.el (tramp-test43-threads): New test.
(tramp-test44-auto-load, tramp-test44-delay-load)
(tramp-test44-recursive-load, tramp-test44-remote-load-path)
(tramp-test45-unload): Rename.
2018-07-23 17:34:47 +02:00
13 changed files with 670 additions and 216 deletions

View file

@ -247,6 +247,36 @@ but will instead just display the buffer's contents before the
changes, and show an echo-area message telling you how to revert the
buffer from the file.
@cindex visiting files asynchronously
@vindex execute-file-commands-asynchronously
Sometimes, it is handy to visit a file asynchronously. This means,
while loading the file into its buffer Emacs keeps responsive, and you
can continue to edit other files, or call commands. This is
controlled by the user option @code{execute-file-commands-asynchronously}.
If this option is @code{nil} (the default), visiting a file is a
synchronous operation. If the value is a regexp, this allows
asynchronously visiting files whose name matches the regexp, otherwise
synchronously. If the value is @code{t}, visiting files is
unconditionally asynchronous.
If you want to visit all remote files asynchronously, you should set
@example
@group
(customize-set-variable
'execute-file-commands-asynchronously tramp-file-name-regexp
"Visit remote files asynchronously")
@end group
@end example
@kindex C-x &
If you type @kbd{C-x &} (@code{universal-async-argument}) prior the
file visiting command, the meaning of
@code{execute-file-commands-asynchronously} will be reverted. If this
user option is @code{nil}, visiting a file is performed
asynchronously. Contrary, if this user option is non-@code{nil},
visiting a file is performed synchronously.
@kindex C-x C-v
@findex find-alternate-file
If you visit the wrong file unintentionally by typing its name

View file

@ -26,6 +26,7 @@ are done, and the subroutines that allow Lisp programs to do them.
* Waiting:: Waiting for user input or elapsed time.
* Quitting:: How @kbd{C-g} works. How to catch or defer quitting.
* Prefix Command Arguments:: How the commands to set prefix args work.
* Prefix Commands:: A way to dispatch commands with an option.
* Recursive Editing:: Entering a recursive edit,
and why you usually shouldn't.
* Disabling Commands:: How the command loop handles disabled commands.
@ -3934,6 +3935,39 @@ command; its value is negated to form the new prefix argument. Don't
call this command yourself unless you know what you are doing.
@end deffn
@node Prefix Commands
@section Prefix Commands
@cindex prefix command
A @dfn{prefix command} is a command which precedes another command.
It dispatches the @emph{next} command by toggling a controlling
variable's value, which could be taken into account then by that next
command. Whether the following command cares about the controlling
variable is up to that command.
Emacs knows of two prefix commands, @code{universal-async-argument}
(@pxref{Visiting,,, emacs, The GNU Emacs Manual}) and
@code{universal-coding-system-argument} (@pxref{Text Coding,,, emacs,
The GNU Emacs Manual}).
@deffn Command universal-async-argument
This prefix command indicates the next command to run asynchronously.
It is up to that next command to decide what asynchronously means, or
to ignore the prefix command.
@end deffn
@defvar universal-async-argument
This is the controlling variable toggled by the
@code{universal-async-argument} command. If a command supports
asynchronous behavior, it should check whether the value of this
variable is non-@code{nil}.
@end defvar
@deffn Command universal-coding-system-argument
This prefix command determines the coding system to be used by the
next I/O command.
@end deffn
@node Recursive Editing
@section Recursive Editing
@cindex recursive command loop

View file

@ -788,6 +788,7 @@ Command Loop
* Waiting:: Waiting for user input or elapsed time.
* Quitting:: How @kbd{C-g} works. How to catch or defer quitting.
* Prefix Command Arguments:: How the commands to set prefix args work.
* Prefix Commands:: A way to dispatch commands with an option.
* Recursive Editing:: Entering a recursive edit,
and why you usually shouldn't.
* Disabling Commands:: How the command loop handles disabled commands.

View file

@ -96,7 +96,7 @@ not alter it, the fastest way is to use @code{insert-file-contents} in a
temporary buffer. Visiting the file is not necessary and takes longer.
@xref{Reading from Files}.
@deffn Command find-file filename &optional wildcards
@deffn Command find-file filename &optional wildcards async
This command selects a buffer visiting the file @var{filename},
using an existing buffer if there is one, and otherwise creating a
new buffer and reading the file into it. It also returns that buffer.
@ -105,7 +105,8 @@ Aside from some technical details, the body of the @code{find-file}
function is basically equivalent to:
@smallexample
(switch-to-buffer (find-file-noselect filename nil nil wildcards))
(switch-to-buffer
(find-file-noselect filename nil nil wildcards async))
@end smallexample
@noindent
@ -115,21 +116,28 @@ If @var{wildcards} is non-@code{nil}, which is always true in an
interactive call, then @code{find-file} expands wildcard characters in
@var{filename} and visits all the matching files.
If @var{async} is non-@code{nil}, the file will be loaded into the
buffer asynchronously. Interactively, this is indicated by either
setting user option @code{execute-file-commands-asynchronously} to
non-@code{nil}, or by typing @kbd{C-x &}
(@code{universal-async-argument}) prior the command.
When @code{find-file} is called interactively, it prompts for
@var{filename} in the minibuffer.
@end deffn
@deffn Command find-file-literally filename
@deffn Command find-file-literally filename &optional async
This command visits @var{filename}, like @code{find-file} does, but it
does not perform any format conversions (@pxref{Format Conversion}),
character code conversions (@pxref{Coding Systems}), or end-of-line
conversions (@pxref{Coding System Basics, End of line conversion}).
The buffer visiting the file is made unibyte, and its major mode is
Fundamental mode, regardless of the file name. File local variable
specifications in the file (@pxref{File Local Variables}) are
ignored, and automatic decompression and adding a newline at the end
of the file due to @code{require-final-newline} (@pxref{Saving
Buffers, require-final-newline}) are also disabled.
If called interactively, the argument @var{async} is determined like
in @code{find-file}. The buffer visiting the file is made unibyte,
and its major mode is Fundamental mode, regardless of the file name.
File local variable specifications in the file (@pxref{File Local
Variables}) are ignored, and automatic decompression and adding a
newline at the end of the file due to @code{require-final-newline}
(@pxref{Saving Buffers, require-final-newline}) are also disabled.
Note that if Emacs already has a buffer visiting the same file
non-literally, it will not visit the same file literally, but instead
@ -139,7 +147,7 @@ buffer and then read the file contents into it using
@code{insert-file-contents-literally} (@pxref{Reading from Files}).
@end deffn
@defun find-file-noselect filename &optional nowarn rawfile wildcards
@defun find-file-noselect filename &optional nowarn rawfile wildcards async
This function is the guts of all the file-visiting functions. It
returns a buffer visiting the file @var{filename}. You may make the
buffer current or display it in a window if you wish, but this
@ -157,7 +165,9 @@ Reading the file involves decoding the file's contents (@pxref{Coding
Systems}), including end-of-line conversion, and format conversion
(@pxref{Format Conversion}). If @var{wildcards} is non-@code{nil},
then @code{find-file-noselect} expands wildcard characters in
@var{filename} and visits all the matching files.
@var{filename} and visits all the matching files. If @var{async} is
non-@code{nil} and there are several matching files due to expansion
of wildcard characters, every file will be loaded in an own thread.
This function displays warning or advisory messages in various peculiar
cases, unless the optional argument @var{nowarn} is non-@code{nil}. For
@ -191,7 +201,7 @@ various files.
@end example
@end defun
@deffn Command find-file-other-window filename &optional wildcards
@deffn Command find-file-other-window filename &optional wildcards async
This command selects a buffer visiting the file @var{filename}, but
does so in a window other than the selected window. It may use
another existing window or split a window; see @ref{Switching
@ -201,7 +211,7 @@ When this command is called interactively, it prompts for
@var{filename}.
@end deffn
@deffn Command find-file-read-only filename &optional wildcards
@deffn Command find-file-read-only filename &optional wildcards async
This command selects a buffer visiting the file @var{filename}, like
@code{find-file}, but it marks the buffer as read-only. @xref{Read Only
Buffers}, for related functions and variables.
@ -219,6 +229,16 @@ the @code{find-file} commands ignore their @var{wildcards} argument
and never treat wildcard characters specially.
@end defopt
@kindex C-x &
@findex universal-async-argument
@defopt execute-file-commands-asynchronously
If this variable is non-@code{nil}, a file will be visited
asynchronously when called interactively. If it is a regular
expression, it must match the file name to be visited. This behavior
is toggled by the command @kbd{C-x &} (@code{universal-async-argument})
prior to the interactive call of the file visiting command.
@end defopt
@defopt find-file-hook
The value of this variable is a list of functions to be called after a
file is visited. The file's local-variables specification (if any) will

View file

@ -76,6 +76,16 @@ mistaken compositions, this will now work as well.
This works like 'kill-matching-buffers', but without asking for
confirmation.
+++
** Files can be visited asynchronously.
If the new user option 'execute-file-commands-asynchronously' has a
non-nil value, interactive file visiting commands load the file
asynchronously into the respective buffer. I.e., Emacs is still
responsive while loading the files, which is useful especially for
remote files. If the value is a regular expression, files matching
this expression are loaded asynchronously. See the node "(emacs)
Visiting" in the user manual for the supported commands.
* Changes in Specialized Modes and Packages in Emacs 30.1
@ -180,6 +190,9 @@ point is not in a comment or a string. It is by default bound to
** Tramp
---
*** Tramp is now thread-safe.
+++
*** New connection method "toolbox".
This allows accessing system containers provided by Toolbox.
@ -245,6 +258,13 @@ hooks named after the feature name, like 'esh-mode-unload-hook'.
* Lisp Changes in Emacs 30.1
--- (Needs better documentation)
** There is a new command 'universal-async-argument', bound to 'C-x &'.
If this command precedes another command, the value of variable
'universal-async-argument' will be toggled. This indicates, that the
following command shall be executed asynchronously. For example,
file visiting commands would load files into buffers asynchronously.
** Functions and variables to transpose sexps
+++

View file

@ -1777,15 +1777,30 @@ rather than FUN itself, to `minibuffer-setup-hook'."
,@body)
(remove-hook 'minibuffer-setup-hook ,hook)))))
(defun find-file-read-args (prompt mustmatch)
(list (read-file-name prompt nil default-directory mustmatch)
t))
(defun find-file-read-args (prompt mustmatch &optional wildcards)
"Return the interactive spec (<filename> <async>).
If WILDCARDS is non-nil, return the spec (<filename> t <async>)."
(let ((filename (read-file-name prompt nil default-directory mustmatch))
(async (and (featurep 'threads)
(xor universal-async-argument
execute-file-commands-asynchronously))))
(when (stringp async) (setq async (string-match-p async filename)))
(if wildcards `(,filename t ,async) `(,filename ,async))))
(defun file-name-history--add (file)
"Add FILE to `file-name-history'."
(add-to-history 'file-name-history (abbreviate-file-name file)))
(defun find-file (filename &optional wildcards)
(defmacro find-file-with-threads (filename async &rest body)
"Run BODY in an own thread, if ASYNC is non-nil."
(declare (indent 2) (debug t))
`(if ,async
(progn
(make-thread (lambda () ,@body) (concat "find-file " ,filename))
(thread-yield))
,@body))
(defun find-file (filename &optional wildcards async)
"Edit file FILENAME.
\\<minibuffer-local-map>Switch to a buffer visiting file FILENAME, creating one if none
already exists.
@ -1811,17 +1826,24 @@ Interactively, or if WILDCARDS is non-nil in a call from Lisp,
expand wildcards (if any) and visit multiple files. You can
suppress wildcard expansion by setting `find-file-wildcards' to nil.
If ASYNC is non-nil, the file will be loaded into the buffer
asynchronously. Interactively, this is indicated by setting
`execute-file-commands-asynchronously' to a proper non-nil value.
This behavior can be toggled by \\[universal-async-argument]
prior the command invocation.
\\<global-map>To visit a file without any kind of conversion and without
automatically choosing a major mode, use \\[find-file-literally]."
(interactive
(find-file-read-args "Find file: "
(confirm-nonexistent-file-or-buffer)))
(let ((value (find-file-noselect filename nil nil wildcards)))
(if (listp value)
(mapcar 'pop-to-buffer-same-window (nreverse value))
(pop-to-buffer-same-window value))))
(confirm-nonexistent-file-or-buffer) t))
(find-file-with-threads filename async
(let ((value (find-file-noselect filename nil nil wildcards async)))
(if (listp value)
(mapcar 'pop-to-buffer-same-window (nreverse value))
(pop-to-buffer-same-window value)))))
(defun find-file-other-window (filename &optional wildcards)
(defun find-file-other-window (filename &optional wildcards async)
"Edit file FILENAME, in another window.
Like \\[find-file] (which see), but creates a new window or reuses
@ -1841,20 +1863,27 @@ current directory to be available on first \\[next-history-element]
request.
Interactively, or if WILDCARDS is non-nil in a call from Lisp,
expand wildcards (if any) and visit multiple files."
expand wildcards (if any) and visit multiple files.
If ASYNC is non-nil, the file will be loaded into the buffer
asynchronously. Interactively, this is indicated by setting
`execute-file-commands-asynchronously' to a proper non-nil value.
This behavior can be toggled by \\[universal-async-argument]
prior the command invocation."
(interactive
(find-file-read-args "Find file in other window: "
(confirm-nonexistent-file-or-buffer)))
(let ((value (find-file-noselect filename nil nil wildcards)))
(if (listp value)
(progn
(setq value (nreverse value))
(switch-to-buffer-other-window (car value))
(mapc 'switch-to-buffer (cdr value))
value)
(switch-to-buffer-other-window value))))
(confirm-nonexistent-file-or-buffer) t))
(find-file-with-threads filename async
(let ((value (find-file-noselect filename nil nil wildcards async)))
(if (listp value)
(progn
(setq value (nreverse value))
(switch-to-buffer-other-window (car value))
(mapc 'switch-to-buffer (cdr value))
value)
(switch-to-buffer-other-window value)))))
(defun find-file-other-frame (filename &optional wildcards)
(defun find-file-other-frame (filename &optional wildcards async)
"Edit file FILENAME, in another frame.
Like \\[find-file] (which see), but creates a new frame or reuses
@ -1874,76 +1903,98 @@ current directory to be available on first \\[next-history-element]
request.
Interactively, or if WILDCARDS is non-nil in a call from Lisp,
expand wildcards (if any) and visit multiple files."
expand wildcards (if any) and visit multiple files.
If ASYNC is non-nil, the file will be loaded into the buffer
asynchronously. Interactively, this is indicated by setting
`execute-file-commands-asynchronously' to a proper non-nil value.
This behavior can be toggled by \\[universal-async-argument]
prior the command invocation."
(interactive
(find-file-read-args "Find file in other frame: "
(confirm-nonexistent-file-or-buffer)))
(let ((value (find-file-noselect filename nil nil wildcards)))
(if (listp value)
(progn
(setq value (nreverse value))
(switch-to-buffer-other-frame (car value))
(mapc 'switch-to-buffer (cdr value))
value)
(switch-to-buffer-other-frame value))))
(confirm-nonexistent-file-or-buffer) t))
(find-file-with-threads filename async
(let ((value (find-file-noselect filename nil nil wildcards async)))
(if (listp value)
(progn
(setq value (nreverse value))
(switch-to-buffer-other-frame (car value))
(mapc 'switch-to-buffer (cdr value))
value)
(switch-to-buffer-other-frame value)))))
(defun find-file-existing (filename)
(defun find-file-existing (filename &optional async)
"Edit the existing file FILENAME.
Like \\[find-file], but allow only a file that exists, and do not allow
file names with wildcards."
(interactive (nbutlast (find-file-read-args "Find existing file: " t)))
file names with wildcards.
If ASYNC is non-nil, the file will be loaded into the buffer
asynchronously. Interactively, this is indicated by setting
`execute-file-commands-asynchronously' to a proper non-nil value.
This behavior can be toggled by \\[universal-async-argument]
prior the command invocation."
(interactive
(find-file-read-args "Find existing file: " t))
(if (and (not (called-interactively-p 'interactive))
(not (file-exists-p filename)))
(error "%s does not exist" filename)
(find-file filename)
(current-buffer)))
(find-file-with-threads filename async
(find-file filename)
(current-buffer))))
(defun find-file--read-only (fun filename wildcards)
(defun find-file--read-only (fun filename wildcards async)
(unless (or (and wildcards find-file-wildcards
(not (file-name-quoted-p filename))
(string-match "[[*?]" filename))
(file-exists-p filename))
(error "%s does not exist" filename))
(let ((value (funcall fun filename wildcards)))
(mapc (lambda (b) (with-current-buffer b (read-only-mode 1)))
(if (listp value) value (list value)))
value))
(find-file-with-threads filename async
(let ((value (funcall fun filename wildcards)))
(mapc (lambda (b) (with-current-buffer b (read-only-mode 1)))
(if (listp value) value (list value)))
value)))
(defun find-file-read-only (filename &optional wildcards)
(defun find-file-read-only (filename &optional wildcards async)
"Edit file FILENAME but don't allow changes.
Like \\[find-file], but marks buffer as read-only.
Use \\[read-only-mode] to permit editing."
(interactive
(find-file-read-args "Find file read-only: "
(confirm-nonexistent-file-or-buffer)))
(find-file--read-only #'find-file filename wildcards))
(confirm-nonexistent-file-or-buffer) t))
(find-file--read-only #'find-file filename wildcards async))
(defun find-file-read-only-other-window (filename &optional wildcards)
(defun find-file-read-only-other-window (filename &optional wildcards async)
"Edit file FILENAME in another window but don't allow changes.
Like \\[find-file-other-window], but marks buffer as read-only.
Use \\[read-only-mode] to permit editing."
(interactive
(find-file-read-args "Find file read-only other window: "
(confirm-nonexistent-file-or-buffer)))
(find-file--read-only #'find-file-other-window filename wildcards))
(confirm-nonexistent-file-or-buffer) t))
(find-file--read-only #'find-file-other-window filename wildcards async))
(defun find-file-read-only-other-frame (filename &optional wildcards)
(defun find-file-read-only-other-frame (filename &optional wildcards async)
"Edit file FILENAME in another frame but don't allow changes.
Like \\[find-file-other-frame], but marks buffer as read-only.
Use \\[read-only-mode] to permit editing."
(interactive
(find-file-read-args "Find file read-only other frame: "
(confirm-nonexistent-file-or-buffer)))
(find-file--read-only #'find-file-other-frame filename wildcards))
(confirm-nonexistent-file-or-buffer) t))
(find-file--read-only #'find-file-other-frame filename wildcards async))
(defun find-alternate-file-other-window (filename &optional wildcards)
(defun find-alternate-file-other-window (filename &optional wildcards async)
"Find file FILENAME as a replacement for the file in the next window.
This command does not select that window.
See \\[find-file] for the possible forms of the FILENAME argument.
Interactively, or if WILDCARDS is non-nil in a call from Lisp,
expand wildcards (if any) and replace the file with multiple files."
expand wildcards (if any) and replace the file with multiple files.
If ASYNC is non-nil, the file will be loaded into the buffer
asynchronously. Interactively, this is indicated by setting
`execute-file-commands-asynchronously' to a proper non-nil value.
This behavior can be toggled by \\[universal-async-argument]
prior the command invocation."
(interactive
(save-selected-window
(other-window 1)
@ -1956,12 +2007,15 @@ expand wildcards (if any) and replace the file with multiple files."
(list (read-file-name
"Find alternate file: " file-dir nil
(confirm-nonexistent-file-or-buffer) file-name)
t))))
t (and (featurep 'threads)
(xor universal-async-argument
execute-file-commands-asynchronously))))))
(when (stringp async) (setq async (string-match-p async filename)))
(if (one-window-p)
(find-file-other-window filename wildcards)
(find-file-other-window filename wildcards async)
(save-selected-window
(other-window 1)
(find-alternate-file filename wildcards))))
(find-alternate-file filename wildcards async))))
;; Defined and used in buffer.c, but not as a DEFVAR_LISP.
(defvar kill-buffer-hook nil
@ -1976,7 +2030,7 @@ INHIBIT-BUFFER-HOOKS non-nil.
Note: Be careful with let-binding this hook considering it is
frequently used for cleanup.")
(defun find-alternate-file (filename &optional wildcards)
(defun find-alternate-file (filename &optional wildcards async)
"Find file FILENAME, select its buffer, kill previous buffer.
If the current buffer now contains an empty file that you just visited
\(presumably by mistake), use this command to visit the file you really want.
@ -1986,6 +2040,12 @@ See \\[find-file] for the possible forms of the FILENAME argument.
Interactively, or if WILDCARDS is non-nil in a call from Lisp,
expand wildcards (if any) and replace the file with multiple files.
If ASYNC is non-nil, the file will be loaded into the buffer
asynchronously. Interactively, this is indicated by setting
`execute-file-commands-asynchronously' to a proper non-nil value.
This behavior can be toggled by \\[universal-async-argument]
prior the command invocation.
If the current buffer is an indirect buffer, or the base buffer
for one or more indirect buffers, the other buffer(s) are not
killed."
@ -1999,7 +2059,10 @@ killed."
(list (read-file-name
"Find alternate file: " file-dir nil
(confirm-nonexistent-file-or-buffer) file-name)
t)))
t (and (featurep 'threads)
(xor universal-async-argument
execute-file-commands-asynchronously)))))
(when (stringp async) (setq async (string-match-p async filename)))
(unless (run-hook-with-args-until-failure 'kill-buffer-query-functions)
(user-error "Aborted"))
(and (buffer-modified-p) buffer-file-name
@ -2040,7 +2103,7 @@ killed."
;; Don't use `find-file' because it may end up using another window
;; in some corner cases, e.g. when the selected window is
;; softly-dedicated.
(let ((newbuf (find-file-noselect filename nil nil wildcards)))
(let ((newbuf (find-file-noselect filename nil nil wildcards async)))
(switch-to-buffer (if (consp newbuf) (car newbuf) newbuf))))
(when (eq obuf (current-buffer))
;; This executes if find-file gets an error
@ -2199,6 +2262,15 @@ suppresses this warning."
:version "21.1"
:type 'boolean)
(defcustom execute-file-commands-asynchronously nil
"Non-nil means visit files asynchronously when called interactively.
If it is a regular expression, it must match the file names to be
visited. This behavior is toggled by \\[universal-async-argument]
prior the command invocation."
:group 'files
:version "28.1"
:type '(choice boolean regexp))
(defcustom large-file-warning-threshold 10000000
"Maximum size of file above which a confirmation is requested.
When nil, never request confirmation."
@ -2332,7 +2404,7 @@ be visible in the echo area."
(apply #'message format args)
(when save-silently (message nil)))
(defun find-file-noselect (filename &optional nowarn rawfile wildcards)
(defun find-file-noselect (filename &optional nowarn rawfile wildcards async)
"Read file FILENAME into a buffer and return the buffer.
If a buffer exists visiting FILENAME, return that one, but
verify that the file has not changed since visited or saved.
@ -2342,7 +2414,11 @@ Optional third arg RAWFILE non-nil means the file is read literally.
Optional fourth arg WILDCARDS non-nil means do wildcard processing
and visit all the matching files. When wildcards are actually
used and expanded, return a list of buffers that are visiting
the various files."
the various files.
If ASYNC is non-nil, the file will be loaded into the buffer
asynchronously. When several files are loaded due to WILDCARDS,
every file will be loaded in an own thread."
(setq filename
(abbreviate-file-name
(expand-file-name filename)))
@ -2361,10 +2437,32 @@ the various files."
(let ((files (condition-case nil
(file-expand-wildcards filename t)
(error (list filename))))
(find-file-wildcards nil))
(find-file-wildcards nil)
threads)
(if (null files)
(find-file-noselect filename)
(mapcar #'find-file-noselect files)))
(if async
(let (result)
;; Create one thread per file.
(setq threads
(mapcar
(lambda (file)
(make-thread
(lambda () (find-file-noselect file))
(concat "find-file-noselect " file)))
files))
;; Collect the results. We use `vc-mutex' here in
;; order to let all `vc-refresh-state' threads run
;; after the file visiting operations.
(with-mutex vc-mutex
(thread-yield)
(dolist (thread threads result)
(setq result (with-demoted-errors
(cons (thread-join thread) result))))))
(mapcar #'find-file-noselect files))))
(let* ((buf (get-file-buffer filename))
(truename (abbreviate-file-name (file-truename filename)))
(attributes (file-attributes truename))
@ -2650,7 +2748,7 @@ This has the `permanent-local' property, which takes effect if you
make the variable buffer-local.")
(put 'find-file-literally 'permanent-local t)
(defun find-file-literally (filename)
(defun find-file-literally (filename &optional async)
"Visit file FILENAME with no conversion of any kind.
Format conversion and character code conversion are both disabled,
and multibyte characters are disabled in the resulting buffer.
@ -2662,6 +2760,12 @@ file due to `require-final-newline' is also disabled.
If Emacs already has a buffer that is visiting the file,
this command asks you whether to visit it literally instead.
If ASYNC is non-nil, the file will be loaded into the buffer
asynchronously. Interactively, this is indicated by setting
`execute-file-commands-asynchronously' to a proper non-nil value.
This behavior can be toggled by \\[universal-async-argument]
prior the command invocation.
In non-interactive use, the value is the buffer where the file is
visited literally. If the file was visited in a buffer before
this command was invoked, it will reuse the existing buffer,
@ -2673,10 +2777,10 @@ In a Lisp program, if you want to be sure of accessing a file's
contents literally, you should create a temporary buffer and then read
the file contents into it using `insert-file-contents-literally'."
(interactive
(list (read-file-name
"Find file literally: " nil default-directory
(confirm-nonexistent-file-or-buffer))))
(switch-to-buffer (find-file-noselect filename nil t)))
(find-file-read-args "Find file literally: "
(confirm-nonexistent-file-or-buffer)))
(find-file-with-threads filename async
(switch-to-buffer (find-file-noselect filename nil t nil async))))
(defun after-find-file (&optional error warn noauto
_after-find-file-from-revert-buffer

View file

@ -531,7 +531,9 @@ PROPERTIES is a list of file properties (strings)."
(prin1-to-string key))
(if (hash-table-p value)
(tramp-cache-print value)
(if (bufferp value)
(if (or (bufferp value)
;; Mutexes have entered Emacs 26.1.
(tramp-compat-funcall 'mutexp value))
(prin1-to-string (prin1-to-string value))
(prin1-to-string value))))))
(setq result (if result (concat result " " tmp) tmp))))

View file

@ -83,6 +83,32 @@ Add the extension of F, if existing."
tramp-temp-name-prefix tramp-compat-temporary-file-directory)
dir-flag (file-name-extension f t)))
;; Threads have entered Emacs 26.1, `main-thread' in Emacs 27.1. But
;; then, they might not exist when Emacs is configured
;; --without-threads.
(defconst tramp-compat-main-thread (bound-and-true-p main-thread)
"The main thread of Emacs, if compiled --with-threads.")
(defsubst tramp-compat-current-thread ()
"The current thread, or nil if compiled --without-threads."
(tramp-compat-funcall 'current-thread))
(defsubst tramp-compat-thread-yield ()
"Yield the CPU to another thread."
(tramp-compat-funcall 'thread-yield))
(defsubst tramp-compat-make-mutex (name)
"Create a mutex."
(tramp-compat-funcall 'make-mutex name))
(defmacro tramp-compat-with-mutex (mutex &rest body)
"Invoke BODY with MUTEX held, releasing MUTEX when done.
This is the simplest safe way to acquire and release a mutex."
(declare (indent 1) (debug t))
`(if (mutexp ,mutex)
(with-mutex ,mutex ,@body)
,@body))
;; `file-modes', `set-file-modes' and `set-file-times' got argument
;; FLAG in Emacs 28.1.
(defalias 'tramp-compat-file-modes

View file

@ -31,6 +31,7 @@
(require 'tramp)
;; Pacify byte-compiler.
(declare-function ange-ftp-ftp-process-buffer "ange-ftp")
(defvar ange-ftp-ftp-name-arg)
(defvar ange-ftp-ftp-name-res)
(defvar ange-ftp-name-format)
@ -125,7 +126,16 @@ pass to the OPERATION."
;; "ftp" method is used in the Tramp file name. So we unset
;; those values.
(ange-ftp-ftp-name-arg "")
ange-ftp-ftp-name-res)
ange-ftp-ftp-name-res
(v (tramp-dissect-file-name
(apply #'tramp-file-name-for-operation operation args) t)))
(setf (tramp-file-name-method v) tramp-ftp-method)
;; Set "process-name" for thread support.
(tramp-set-connection-property
v "process-name"
(ange-ftp-ftp-process-buffer
(tramp-file-name-host v) (tramp-file-name-user v)))
(cond
;; If argument is a symlink, `file-directory-p' and
;; `file-exists-p' call the traversed file recursively. So we
@ -147,9 +157,7 @@ pass to the OPERATION."
(lambda (operation &rest args)
(tramp-archive-run-real-handler operation args))))
(prog1 (apply #'ange-ftp-hook-function operation args)
(let ((v (tramp-dissect-file-name (car args) t)))
(setf (tramp-file-name-method v) tramp-ftp-method)
(tramp-set-connection-property v "started" t)))))
(tramp-set-connection-property v "started" t))))
;; If the second argument of `copy-file' or `rename-file' is a
;; remote file name but via FTP, ange-ftp doesn't check this.

View file

@ -2107,6 +2107,10 @@ ARGUMENTS to actually emit the message (if applicable)."
(insert "\n"))
;; Timestamp.
(insert (format-time-string "%T.%6N "))
;; Threads.
(unless (or (null tramp-compat-main-thread)
(eq (tramp-compat-current-thread) tramp-compat-main-thread))
(insert (format "%s " (tramp-compat-current-thread))))
;; Calling Tramp function. We suppress compat and trace
;; functions from being displayed.
(let ((btn 1) btf fn)
@ -2684,11 +2688,23 @@ Must be handled by the callers."
res (cdr elt))))
res)))
(defvar tramp-mutex (tramp-compat-make-mutex "tramp")
"Global mutex for Tramp threads.")
(defun tramp-get-mutex (vec)
"Return the mutex locking Tramp threads for VEC."
(if-let ((p (and (tramp-connectable-p vec)
(tramp-get-connection-process vec))))
(with-tramp-connection-property p "mutex"
(tramp-compat-make-mutex (process-name p)))
tramp-mutex))
;; Main function.
;;;###autoload
(defun tramp-file-name-handler (operation &rest args)
"Invoke Tramp file name handler for OPERATION and ARGS.
Fall back to normal file name handler if no Tramp file name handler exists."
Fall back to normal file name handler if no Tramp file name handler exists.
If Emacs is compiled --with-threads, the body is protected by a mutex."
(let ((filename (apply #'tramp-file-name-for-operation operation args))
;; `file-remote-p' is called for everything, even for symbolic
;; links which look remote. We don't want to get an error.
@ -2697,79 +2713,95 @@ Fall back to normal file name handler if no Tramp file name handler exists."
(save-match-data
(setq filename (tramp-replace-environment-variables filename))
(with-parsed-tramp-file-name filename nil
(let ((current-connection tramp-current-connection)
(foreign
(tramp-find-foreign-file-name-handler v operation))
(signal-hook-function #'tramp-signal-hook-function)
result)
;; Set `tramp-current-connection'.
(unless
(tramp-file-name-equal-p v (car tramp-current-connection))
(setq tramp-current-connection (list v)))
;; Call the backend function.
(unwind-protect
(if foreign
(let ((sf (symbol-function foreign)))
;; Some packages set the default directory to
;; a remote path, before respective Tramp
;; packages are already loaded. This results
;; in recursive loading. Therefore, we load
;; the Tramp packages locally.
(when (autoloadp sf)
;; FIXME: Not clear why we need these bindings here.
;; The explanation above is not convincing and
;; the bug#9114 for which it was added doesn't
;; clarify the core of the problem.
(let ((default-directory
tramp-compat-temporary-file-directory)
file-name-handler-alist)
(autoload-do-load sf foreign)))
;; (tramp-message
;; v 4 "Running `%s'..." (cons operation args))
;; If `non-essential' is non-nil, Tramp shall
;; not open a new connection.
;; If Tramp detects that it shouldn't continue
;; to work, it throws the `suppress' event.
;; This could happen for example, when Tramp
;; tries to open the same connection twice in
;; a short time frame.
;; In both cases, we try the default handler then.
(setq result
(catch 'non-essential
(catch 'suppress
(apply foreign operation args))))
;; (tramp-message
;; v 4 "Running `%s'...`%s'" (cons operation args) result)
(cond
((eq result 'non-essential)
(tramp-message
v 5 "Non-essential received in operation %s"
(cons operation args))
(let ((tramp-verbose 10)) (tramp-backtrace v))
(tramp-run-real-handler operation args))
((eq result 'suppress)
(let ((inhibit-message t))
(tramp-message
v 1 "Suppress received in operation %s"
(cons operation args))
(tramp-cleanup-connection v t)
(tramp-run-real-handler operation args)))
(t result)))
;; Nothing to do for us. However, since we are in
;; `tramp-mode', we must suppress the volume
;; letter on MS Windows.
(setq result (tramp-run-real-handler operation args))
(if (stringp result)
(tramp-drop-volume-letter result)
result))
;; Reset `tramp-current-connection'.
;; Give other threads a chance.
(tramp-compat-thread-yield)
;; The mutex allows concurrent run of operations. It
;; guarantees, that the threads are not mixed.
(tramp-compat-with-mutex (tramp-get-mutex v)
;; Run only when Emacs is idle.
(tramp-compat-funcall 'check-idle-thread)
(let ((current-connection tramp-current-connection)
(foreign (tramp-find-foreign-file-name-handler v operation))
(signal-hook-function #'tramp-signal-hook-function)
result)
;; Set `tramp-current-connection'.
(unless
(tramp-file-name-equal-p
(car current-connection) (car tramp-current-connection))
(setq tramp-current-connection current-connection))))))
(tramp-file-name-equal-p v (car tramp-current-connection))
(setq tramp-current-connection (list v)))
;; Call the backend function.
(unwind-protect
(if foreign
(let ((sf (symbol-function foreign))
p)
;; Some packages set the default directory
;; to a remote path, before respective Tramp
;; packages are already loaded. This
;; results in recursive loading. Therefore,
;; we load the Tramp packages locally.
(when (autoloadp sf)
;; FIXME: Not clear why we need these bindings here.
;; The explanation above is not convincing and
;; the bug#9114 for which it was added doesn't
;; clarify the core of the problem.
(let ((default-directory
tramp-compat-temporary-file-directory)
file-name-handler-alist)
(autoload-do-load sf foreign)))
;; (tramp-message
;; v 4 "Running `%s'..." (cons operation args))
;; Switch process thread.
(when (and tramp-mutex
(tramp-connectable-p v)
(setq p (tramp-get-connection-process v)))
(tramp-compat-funcall
'set-process-thread
p (tramp-compat-current-thread)))
;; If `non-essential' is non-nil, Tramp
;; shall not open a new connection.
;; If Tramp detects that it shouldn't
;; continue to work, it throws the
;; `suppress' event. This could happen for
;; example, when Tramp tries to open the
;; same connection twice in a short time
;; frame.
;; In both cases, we try the default handler
;; then.
(setq result
(catch 'non-essential
(catch 'suppress
(apply foreign operation args))))
;; (tramp-message
;; v 4 "Running `%s'...`%s'"
;; (cons operation args) result)
(cond
((eq result 'non-essential)
(tramp-message
v 5 "Non-essential received in operation %s"
(cons operation args))
(tramp-run-real-handler operation args))
((eq result 'suppress)
(let ((inhibit-message t))
(tramp-message
v 1 "Suppress received in operation %s"
(cons operation args))
(tramp-cleanup-connection v t)
(tramp-run-real-handler operation args)))
(t result)))
;; Nothing to do for us. However, since we are
;; in `tramp-mode', we must suppress the volume
;; letter on MS Windows.
(setq result (tramp-run-real-handler operation args))
(if (stringp result)
(tramp-drop-volume-letter result)
result))
;; Reset `tramp-current-connection'.
(unless
(tramp-file-name-equal-p
(car current-connection) (car tramp-current-connection))
(setq tramp-current-connection current-connection)))))))
;; When `tramp-mode' is not enabled, or the file name is quoted,
;; we don't do anything.

View file

@ -5449,6 +5449,53 @@ These commands include \\[set-mark-command] and \\[start-kbd-macro]."
(t
digit))))
(universal-argument--mode))
(defvar universal-async-argument nil
"Non-nil means a command invoked interactively can run asynchronously.
It is run asynchronously only if the command also allows that.
The semantics depend on the command. This variable should not be
set globally, it should be used in let-bindings only.")
(defun universal-async-argument ()
"Execute an interactive command asynchronously."
(interactive)
(let* ((universal-async-argument (not universal-async-argument))
(keyseq (read-key-sequence nil t))
(cmd (key-binding keyseq))
prefix)
;; `read-key-sequence' ignores quit, so make an explicit check.
(if (equal last-input-event (nth 3 (current-input-mode)))
(keyboard-quit))
(when (memq cmd '(universal-argument digit-argument negative-argument))
(call-interactively cmd)
;; Process keys bound in `universal-argument-map'.
(while (progn
(setq keyseq (read-key-sequence nil t)
cmd (key-binding keyseq t))
(not (eq cmd 'universal-argument-other-key)))
(let ((current-prefix-arg prefix-arg)
;; Have to bind `last-command-event' here so that
;; `digit-argument', for instance, can compute the
;; `prefix-arg'.
(last-command-event (aref keyseq 0)))
(call-interactively cmd)))
;; This is the final call to `universal-argument-other-key', which
;; sets the final `prefix-arg'.
(let ((current-prefix-arg prefix-arg))
(call-interactively cmd))
;; Read the command to execute with the given `prefix-arg'.
(setq prefix prefix-arg
keyseq (read-key-sequence nil t)
cmd (key-binding keyseq)))
(let ((current-prefix-arg prefix))
(message "")
(call-interactively cmd))))
(define-key ctl-x-map "&" 'universal-async-argument)
(defvar filter-buffer-substring-functions nil

View file

@ -774,6 +774,10 @@ current, and kill the buffer that visits the link."
(defun vc-default-find-file-hook (_backend)
nil)
;;;###autoload
(defvar vc-mutex (make-mutex "vc")
"Mutex for vc operations.")
(defun vc-refresh-state ()
"Refresh the VC state of the current buffer's file.
@ -781,63 +785,82 @@ This command is more thorough than `vc-state-refresh', in that it
also supports switching a back-end or removing the file from VC.
In the latter case, VC mode is deactivated for this buffer."
(interactive)
;; Recompute whether file is version controlled,
;; if user has killed the buffer and revisited.
;; Recompute whether file is version controlled, if user has killed
;; the buffer and revisited.
(when vc-mode
(setq vc-mode nil))
(when buffer-file-name
(vc-file-clearprops buffer-file-name)
;; FIXME: Why use a hook? Why pass it buffer-file-name?
(add-hook 'vc-mode-line-hook #'vc-mode-line nil t)
(let (backend)
(cond
((setq backend (with-demoted-errors "VC refresh error: %S"
(vc-backend buffer-file-name)))
;; Let the backend setup any buffer-local things he needs.
(vc-call-backend backend 'find-file-hook)
;; Compute the state and put it in the mode line.
(vc-mode-line buffer-file-name backend)
(unless vc-make-backup-files
;; Use this variable, not make-backup-files,
;; because this is for things that depend on the file name.
(setq-local backup-inhibited t)))
((let* ((truename (and buffer-file-truename
(expand-file-name buffer-file-truename)))
(link-type (and truename
(not (equal buffer-file-name truename))
(vc-backend truename))))
(cond ((not link-type) nil) ;Nothing to do.
((eq vc-follow-symlinks nil)
(message
"Warning: symbolic link to %s-controlled source file" link-type))
((or (not (eq vc-follow-symlinks 'ask))
;; Assume we cannot ask, default to yes.
noninteractive
;; Copied from server-start. Seems like there should
;; be a better way to ask "can we get user input?"...
(and (daemonp)
(null (cdr (frame-list)))
(eq (selected-frame) terminal-frame))
;; If we already visited this file by following
;; the link, don't ask again if we try to visit
;; it again. GUD does that, and repeated questions
;; are painful.
(get-file-buffer
(abbreviate-file-name
(file-chase-links buffer-file-name))))
;; Run it asynchronously.
(make-thread
(lambda ()
;; Wait, until the file visiting function tells us so.
(with-mutex vc-mutex)
(vc-file-clearprops buffer-file-name)
(vc-file-clearprops buffer-file-name)
;; FIXME: Why use a hook? Why pass it buffer-file-name?
(add-hook 'vc-mode-line-hook #'vc-mode-line nil t)
(let (backend)
(cond
((setq backend (with-demoted-errors "VC refresh error: %S"
(vc-backend buffer-file-name)))
;; Let the backend setup any buffer-local things he needs.
(vc-call-backend backend 'find-file-hook)
;; Compute the state and put it in the mode line.
(vc-mode-line buffer-file-name backend)
(unless vc-make-backup-files
;; Use this variable, not make-backup-files, because this
;; is for things that depend on the file name.
(setq-local backup-inhibited t)))
((let* ((truename (and buffer-file-truename
(expand-file-name buffer-file-truename)))
(link-type (and truename
(not (equal buffer-file-name truename))
(vc-backend truename))))
(cond ((not link-type) nil) ;Nothing to do.
((eq vc-follow-symlinks nil)
(message
"Warning: symbolic link to %s-controlled source file"
link-type))
((or (not (eq vc-follow-symlinks 'ask))
;; Assume we cannot ask, default to yes.
noninteractive
;; Copied from server-start. Seems like there
;; should be a better way to ask "can we get
;; user input?"...
(and (daemonp)
(null (cdr (frame-list)))
(eq (selected-frame) terminal-frame))
;; If we already visited this file by
;; following the link, don't ask again if we
;; try to visit it again. GUD does that, and
;; repeated questions are painful.
(get-file-buffer
(abbreviate-file-name
(file-chase-links buffer-file-name))))
(vc-follow-link)
(message "Followed link to %s" buffer-file-name)
(vc-refresh-state))
(t
(if (yes-or-no-p (format
"Symbolic link to %s-controlled source file; follow link? " link-type))
(progn (vc-follow-link)
(message "Followed link to %s" buffer-file-name)
(vc-refresh-state))
(message
"Warning: editing through the link bypasses version control")
)))))))))
(vc-follow-link)
(message "Followed link to %s" buffer-file-name)
(vc-refresh-state))
(t
(if (yes-or-no-p
(format
(concat
"Symbolic link to %s-controlled source file; "
"follow link? ")
link-type))
(progn (vc-follow-link)
(message
"Followed link to %s" buffer-file-name)
(vc-refresh-state))
(message
(concat
"Warning: editing through the link "
"bypasses version control"))))))))))
;; The thread name.
(concat "vc-refresh-state " (buffer-name)))
;; Give other threads a chance to run.
(thread-yield)))
(add-hook 'find-file-hook #'vc-refresh-state)
(define-obsolete-function-alias 'vc-find-file-hook #'vc-refresh-state "25.1")

View file

@ -7681,8 +7681,114 @@ process sentinels. They shall not disturb each other."
(let ((auth-sources `(,netrc-file)))
(should (file-exists-p ert-remote-temporary-file-directory)))))))))
(ert-deftest tramp-test48-threads ()
"Check that Tramp cooperates with threads."
(skip-unless (tramp--test-enabled))
(skip-unless (featurep 'threads))
(skip-unless (= (length (with-no-warnings (all-threads))) 1))
(skip-unless (not (with-no-warnings (thread-last-error))))
(skip-unless (boundp main-thread))
;; For the time being it works only in the feature branch.
(skip-unless
(string-equal (or emacs-repository-branch "") "feature/tramp-thread-safe"))
(tramp--test-instrument-test-case 0
(with-no-warnings
(with-timeout (60 (tramp--test-timeout-handler))
;; We cannot bind the variables dynamically; they are used in the threads.
(defvar tmp-name1 (tramp--test-make-temp-name))
(defvar tmp-name2 (tramp--test-make-temp-name))
(defvar tmp-mutex (make-mutex "mutex"))
(defvar tmp-condvar1 (make-condition-variable tmp-mutex "condvar1"))
(defvar tmp-condvar2 (make-condition-variable tmp-mutex "condvar2"))
;; Rename simple file.
(unwind-protect
(let (tmp-thread1 tmp-thread2)
(write-region "foo" nil tmp-name1)
(should (file-exists-p tmp-name1))
(should-not (file-exists-p tmp-name2))
(should (mutexp tmp-mutex))
(should (condition-variable-p tmp-condvar1))
(should (condition-variable-p tmp-condvar2))
;; This thread renames `tmp-name1' to `tmp-name2' twice.
(setq
tmp-thread1
(make-thread
(lambda ()
;; Rename first time.
(rename-file tmp-name1 tmp-name2)
;; Notify thread2.
(with-mutex (condition-mutex tmp-condvar2)
(condition-notify tmp-condvar2 t))
;; Rename second time, once we've got notification from thread2.
(with-mutex (condition-mutex tmp-condvar1)
(condition-wait tmp-condvar1))
(rename-file tmp-name1 tmp-name2))
"thread1"))
(should (threadp tmp-thread1))
(should (thread-live-p tmp-thread1))
;; This thread renames `tmp-name2' to `tmp-name1' twice.
(setq
tmp-thread2
(make-thread
(lambda ()
;; Rename first time, once we've got notification from thread1.
(with-mutex (condition-mutex tmp-condvar2)
(condition-wait tmp-condvar2))
(rename-file tmp-name2 tmp-name1)
;; Notify thread1.
(with-mutex (condition-mutex tmp-condvar1)
(condition-notify tmp-condvar1 t))
;; Rename second time, once we've got notification from
;; the main thread.
(with-mutex (condition-mutex tmp-condvar2)
(condition-wait tmp-condvar2))
(rename-file tmp-name2 tmp-name1))
"thread2"))
(should (threadp tmp-thread2))
(should (thread-live-p tmp-thread2))
(should (= (length (all-threads)) 3))
;; Wait for thread1.
(thread-join tmp-thread1)
;; Checks.
(should-not (thread-live-p tmp-thread1))
(should (= (length (all-threads)) 2))
(should-not (thread-last-error))
(should (file-exists-p tmp-name2))
(should-not (file-exists-p tmp-name1))
;; Notify thread2.
(with-mutex (condition-mutex tmp-condvar2)
(condition-notify tmp-condvar2 t))
;; Wait for thread2.
(thread-join tmp-thread2)
;; Checks.
(should-not (thread-live-p tmp-thread2))
(should (= (length (all-threads)) 1))
(should-not (thread-last-error))
(should (file-exists-p tmp-name1))
(should-not (file-exists-p tmp-name2)))
;; Cleanup.
(ignore-errors (delete-file tmp-name1))
(ignore-errors (delete-file tmp-name2))
;; We could have spurious threads still running; wait for them to die.
(while (cdr (all-threads))
(thread-signal (cadr (all-threads)) 'error nil)
(thread-yield))
;; Cleanup errors.
(ignore-errors (thread-last-error 'cleanup)))))))
;; This test is inspired by Bug#29163.
(ert-deftest tramp-test48-auto-load ()
(ert-deftest tramp-test49-auto-load ()
"Check that Tramp autoloads properly."
;; If we use another syntax but `default', Tramp is already loaded
;; due to the `tramp-change-syntax' call.
@ -7707,7 +7813,7 @@ process sentinels. They shall not disturb each other."
(mapconcat #'shell-quote-argument load-path " -L ")
(shell-quote-argument code)))))))
(ert-deftest tramp-test48-delay-load ()
(ert-deftest tramp-test49-delay-load ()
"Check that Tramp is loaded lazily, only when needed."
;; Tramp is neither loaded at Emacs startup, nor when completing a
;; non-Tramp file name like "/foo". Completing a Tramp-alike file
@ -7737,7 +7843,7 @@ process sentinels. They shall not disturb each other."
(mapconcat #'shell-quote-argument load-path " -L ")
(shell-quote-argument (format code tm)))))))))
(ert-deftest tramp-test48-recursive-load ()
(ert-deftest tramp-test49-recursive-load ()
"Check that Tramp does not fail due to recursive load."
(skip-unless (tramp--test-enabled))
@ -7761,7 +7867,7 @@ process sentinels. They shall not disturb each other."
(mapconcat #'shell-quote-argument load-path " -L ")
(shell-quote-argument code))))))))
(ert-deftest tramp-test48-remote-load-path ()
(ert-deftest tramp-test49-remote-load-path ()
"Check that Tramp autoloads its packages with remote `load-path'."
;; `tramp-cleanup-all-connections' is autoloaded from tramp-cmds.el.
;; It shall still work, when a remote file name is in the
@ -7786,7 +7892,7 @@ process sentinels. They shall not disturb each other."
(mapconcat #'shell-quote-argument load-path " -L ")
(shell-quote-argument code)))))))
(ert-deftest tramp-test49-unload ()
(ert-deftest tramp-test50-unload ()
"Check that Tramp and its subpackages unload completely.
Since it unloads Tramp, it shall be the last test to run."
:tags '(:expensive-test)
@ -7900,6 +8006,7 @@ If INTERACTIVE is non-nil, the tests are run interactively."
;; async processes. Check, why they don't run stable.
;; * Check, why direct async processes do not work for
;; `tramp-test45-asynchronous-requests'.
;; * Fix `tramp-test48-threads'.
(provide 'tramp-tests)