forked from Github/emacs
Compare commits
50 commits
master
...
feature/tr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a22f5ac9a | ||
|
|
b14cd8380e | ||
|
|
a4972d96f9 | ||
|
|
47156f117b | ||
|
|
ac98caa0cd | ||
|
|
47278d3724 | ||
|
|
90ae1ae776 | ||
|
|
ec175a4a49 | ||
|
|
c7e311e5ba | ||
|
|
1488024713 | ||
|
|
80da4fa750 | ||
|
|
25f77f9085 | ||
|
|
c8ca97af38 | ||
|
|
853ade4a65 | ||
|
|
89b80ad4bc | ||
|
|
61be02000e | ||
|
|
c291336562 | ||
|
|
bedde1fc7a | ||
|
|
0361de2f5f | ||
|
|
1ed8d0030b | ||
|
|
279618b685 | ||
|
|
7e6b0b33c0 | ||
|
|
06a2f822a2 | ||
|
|
a692095fd8 | ||
|
|
162353c45c | ||
|
|
9432b70f76 | ||
|
|
2ff7017956 | ||
|
|
5df7423b4a | ||
|
|
43cac37e08 | ||
|
|
8068f14b5f | ||
|
|
646ad72871 | ||
|
|
8f3b6e962d | ||
|
|
c8b5b16eec | ||
|
|
ef57169b99 | ||
|
|
b823346b65 | ||
|
|
8db0de97ac | ||
|
|
33363e5f62 | ||
|
|
d1f39ebc91 | ||
|
|
d7f3187c2e | ||
|
|
11ff7fa18c | ||
|
|
4615b9148c | ||
|
|
d7314b35ff | ||
|
|
4d340cd956 | ||
|
|
bba62df633 | ||
|
|
c6db5347c5 | ||
|
|
0de175d231 | ||
|
|
18c46ed2dd | ||
|
|
917a542d72 | ||
|
|
80acd88e50 | ||
|
|
cf4e1b18aa |
13 changed files with 670 additions and 216 deletions
|
|
@ -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
|
changes, and show an echo-area message telling you how to revert the
|
||||||
buffer from the file.
|
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
|
@kindex C-x C-v
|
||||||
@findex find-alternate-file
|
@findex find-alternate-file
|
||||||
If you visit the wrong file unintentionally by typing its name
|
If you visit the wrong file unintentionally by typing its name
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ are done, and the subroutines that allow Lisp programs to do them.
|
||||||
* Waiting:: Waiting for user input or elapsed time.
|
* Waiting:: Waiting for user input or elapsed time.
|
||||||
* Quitting:: How @kbd{C-g} works. How to catch or defer quitting.
|
* Quitting:: How @kbd{C-g} works. How to catch or defer quitting.
|
||||||
* Prefix Command Arguments:: How the commands to set prefix args work.
|
* 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,
|
* Recursive Editing:: Entering a recursive edit,
|
||||||
and why you usually shouldn't.
|
and why you usually shouldn't.
|
||||||
* Disabling Commands:: How the command loop handles disabled commands.
|
* 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.
|
call this command yourself unless you know what you are doing.
|
||||||
@end deffn
|
@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
|
@node Recursive Editing
|
||||||
@section Recursive Editing
|
@section Recursive Editing
|
||||||
@cindex recursive command loop
|
@cindex recursive command loop
|
||||||
|
|
|
||||||
|
|
@ -788,6 +788,7 @@ Command Loop
|
||||||
* Waiting:: Waiting for user input or elapsed time.
|
* Waiting:: Waiting for user input or elapsed time.
|
||||||
* Quitting:: How @kbd{C-g} works. How to catch or defer quitting.
|
* Quitting:: How @kbd{C-g} works. How to catch or defer quitting.
|
||||||
* Prefix Command Arguments:: How the commands to set prefix args work.
|
* 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,
|
* Recursive Editing:: Entering a recursive edit,
|
||||||
and why you usually shouldn't.
|
and why you usually shouldn't.
|
||||||
* Disabling Commands:: How the command loop handles disabled commands.
|
* Disabling Commands:: How the command loop handles disabled commands.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
temporary buffer. Visiting the file is not necessary and takes longer.
|
||||||
@xref{Reading from Files}.
|
@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},
|
This command selects a buffer visiting the file @var{filename},
|
||||||
using an existing buffer if there is one, and otherwise creating a
|
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.
|
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:
|
function is basically equivalent to:
|
||||||
|
|
||||||
@smallexample
|
@smallexample
|
||||||
(switch-to-buffer (find-file-noselect filename nil nil wildcards))
|
(switch-to-buffer
|
||||||
|
(find-file-noselect filename nil nil wildcards async))
|
||||||
@end smallexample
|
@end smallexample
|
||||||
|
|
||||||
@noindent
|
@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
|
interactive call, then @code{find-file} 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}, 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
|
When @code{find-file} is called interactively, it prompts for
|
||||||
@var{filename} in the minibuffer.
|
@var{filename} in the minibuffer.
|
||||||
@end deffn
|
@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
|
This command visits @var{filename}, like @code{find-file} does, but it
|
||||||
does not perform any format conversions (@pxref{Format Conversion}),
|
does not perform any format conversions (@pxref{Format Conversion}),
|
||||||
character code conversions (@pxref{Coding Systems}), or end-of-line
|
character code conversions (@pxref{Coding Systems}), or end-of-line
|
||||||
conversions (@pxref{Coding System Basics, End of line conversion}).
|
conversions (@pxref{Coding System Basics, End of line conversion}).
|
||||||
The buffer visiting the file is made unibyte, and its major mode is
|
If called interactively, the argument @var{async} is determined like
|
||||||
Fundamental mode, regardless of the file name. File local variable
|
in @code{find-file}. The buffer visiting the file is made unibyte,
|
||||||
specifications in the file (@pxref{File Local Variables}) are
|
and its major mode is Fundamental mode, regardless of the file name.
|
||||||
ignored, and automatic decompression and adding a newline at the end
|
File local variable specifications in the file (@pxref{File Local
|
||||||
of the file due to @code{require-final-newline} (@pxref{Saving
|
Variables}) are ignored, and automatic decompression and adding a
|
||||||
Buffers, require-final-newline}) are also disabled.
|
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
|
Note that if Emacs already has a buffer visiting the same file
|
||||||
non-literally, it will not visit the same file literally, but instead
|
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}).
|
@code{insert-file-contents-literally} (@pxref{Reading from Files}).
|
||||||
@end deffn
|
@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
|
This function is the guts of all the file-visiting functions. It
|
||||||
returns a buffer visiting the file @var{filename}. You may make the
|
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
|
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
|
Systems}), including end-of-line conversion, and format conversion
|
||||||
(@pxref{Format Conversion}). If @var{wildcards} is non-@code{nil},
|
(@pxref{Format Conversion}). If @var{wildcards} is non-@code{nil},
|
||||||
then @code{find-file-noselect} expands wildcard characters in
|
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
|
This function displays warning or advisory messages in various peculiar
|
||||||
cases, unless the optional argument @var{nowarn} is non-@code{nil}. For
|
cases, unless the optional argument @var{nowarn} is non-@code{nil}. For
|
||||||
|
|
@ -191,7 +201,7 @@ various files.
|
||||||
@end example
|
@end example
|
||||||
@end defun
|
@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
|
This command selects a buffer visiting the file @var{filename}, but
|
||||||
does so in a window other than the selected window. It may use
|
does so in a window other than the selected window. It may use
|
||||||
another existing window or split a window; see @ref{Switching
|
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}.
|
@var{filename}.
|
||||||
@end deffn
|
@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
|
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
|
@code{find-file}, but it marks the buffer as read-only. @xref{Read Only
|
||||||
Buffers}, for related functions and variables.
|
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.
|
and never treat wildcard characters specially.
|
||||||
@end defopt
|
@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
|
@defopt find-file-hook
|
||||||
The value of this variable is a list of functions to be called after a
|
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
|
file is visited. The file's local-variables specification (if any) will
|
||||||
|
|
|
||||||
20
etc/NEWS
20
etc/NEWS
|
|
@ -76,6 +76,16 @@ mistaken compositions, this will now work as well.
|
||||||
This works like 'kill-matching-buffers', but without asking for
|
This works like 'kill-matching-buffers', but without asking for
|
||||||
confirmation.
|
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
|
* 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
|
||||||
|
|
||||||
|
---
|
||||||
|
*** Tramp is now thread-safe.
|
||||||
|
|
||||||
+++
|
+++
|
||||||
*** New connection method "toolbox".
|
*** New connection method "toolbox".
|
||||||
This allows accessing system containers provided by 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
|
* 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
|
** Functions and variables to transpose sexps
|
||||||
|
|
||||||
+++
|
+++
|
||||||
|
|
|
||||||
238
lisp/files.el
238
lisp/files.el
|
|
@ -1777,15 +1777,30 @@ rather than FUN itself, to `minibuffer-setup-hook'."
|
||||||
,@body)
|
,@body)
|
||||||
(remove-hook 'minibuffer-setup-hook ,hook)))))
|
(remove-hook 'minibuffer-setup-hook ,hook)))))
|
||||||
|
|
||||||
(defun find-file-read-args (prompt mustmatch)
|
(defun find-file-read-args (prompt mustmatch &optional wildcards)
|
||||||
(list (read-file-name prompt nil default-directory mustmatch)
|
"Return the interactive spec (<filename> <async>).
|
||||||
t))
|
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)
|
(defun file-name-history--add (file)
|
||||||
"Add FILE to `file-name-history'."
|
"Add FILE to `file-name-history'."
|
||||||
(add-to-history 'file-name-history (abbreviate-file-name file)))
|
(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.
|
"Edit file FILENAME.
|
||||||
\\<minibuffer-local-map>Switch to a buffer visiting file FILENAME, creating one if none
|
\\<minibuffer-local-map>Switch to a buffer visiting file FILENAME, creating one if none
|
||||||
already exists.
|
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
|
expand wildcards (if any) and visit multiple files. You can
|
||||||
suppress wildcard expansion by setting `find-file-wildcards' to nil.
|
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
|
\\<global-map>To visit a file without any kind of conversion and without
|
||||||
automatically choosing a major mode, use \\[find-file-literally]."
|
automatically choosing a major mode, use \\[find-file-literally]."
|
||||||
(interactive
|
(interactive
|
||||||
(find-file-read-args "Find file: "
|
(find-file-read-args "Find file: "
|
||||||
(confirm-nonexistent-file-or-buffer)))
|
(confirm-nonexistent-file-or-buffer) t))
|
||||||
(let ((value (find-file-noselect filename nil nil wildcards)))
|
(find-file-with-threads filename async
|
||||||
(if (listp value)
|
(let ((value (find-file-noselect filename nil nil wildcards async)))
|
||||||
(mapcar 'pop-to-buffer-same-window (nreverse value))
|
(if (listp value)
|
||||||
(pop-to-buffer-same-window 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.
|
"Edit file FILENAME, in another window.
|
||||||
|
|
||||||
Like \\[find-file] (which see), but creates a new window or reuses
|
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.
|
request.
|
||||||
|
|
||||||
Interactively, or if WILDCARDS is non-nil in a call from Lisp,
|
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
|
(interactive
|
||||||
(find-file-read-args "Find file in other window: "
|
(find-file-read-args "Find file in other window: "
|
||||||
(confirm-nonexistent-file-or-buffer)))
|
(confirm-nonexistent-file-or-buffer) t))
|
||||||
(let ((value (find-file-noselect filename nil nil wildcards)))
|
(find-file-with-threads filename async
|
||||||
(if (listp value)
|
(let ((value (find-file-noselect filename nil nil wildcards async)))
|
||||||
(progn
|
(if (listp value)
|
||||||
(setq value (nreverse value))
|
(progn
|
||||||
(switch-to-buffer-other-window (car value))
|
(setq value (nreverse value))
|
||||||
(mapc 'switch-to-buffer (cdr value))
|
(switch-to-buffer-other-window (car value))
|
||||||
value)
|
(mapc 'switch-to-buffer (cdr value))
|
||||||
(switch-to-buffer-other-window 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.
|
"Edit file FILENAME, in another frame.
|
||||||
|
|
||||||
Like \\[find-file] (which see), but creates a new frame or reuses
|
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.
|
request.
|
||||||
|
|
||||||
Interactively, or if WILDCARDS is non-nil in a call from Lisp,
|
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
|
(interactive
|
||||||
(find-file-read-args "Find file in other frame: "
|
(find-file-read-args "Find file in other frame: "
|
||||||
(confirm-nonexistent-file-or-buffer)))
|
(confirm-nonexistent-file-or-buffer) t))
|
||||||
(let ((value (find-file-noselect filename nil nil wildcards)))
|
(find-file-with-threads filename async
|
||||||
(if (listp value)
|
(let ((value (find-file-noselect filename nil nil wildcards async)))
|
||||||
(progn
|
(if (listp value)
|
||||||
(setq value (nreverse value))
|
(progn
|
||||||
(switch-to-buffer-other-frame (car value))
|
(setq value (nreverse value))
|
||||||
(mapc 'switch-to-buffer (cdr value))
|
(switch-to-buffer-other-frame (car value))
|
||||||
value)
|
(mapc 'switch-to-buffer (cdr value))
|
||||||
(switch-to-buffer-other-frame 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.
|
"Edit the existing file FILENAME.
|
||||||
Like \\[find-file], but allow only a file that exists, and do not allow
|
Like \\[find-file], but allow only a file that exists, and do not allow
|
||||||
file names with wildcards."
|
file names with wildcards.
|
||||||
(interactive (nbutlast (find-file-read-args "Find existing file: " t)))
|
|
||||||
|
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))
|
(if (and (not (called-interactively-p 'interactive))
|
||||||
(not (file-exists-p filename)))
|
(not (file-exists-p filename)))
|
||||||
(error "%s does not exist" filename)
|
(error "%s does not exist" filename)
|
||||||
(find-file filename)
|
(find-file-with-threads filename async
|
||||||
(current-buffer)))
|
(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
|
(unless (or (and wildcards find-file-wildcards
|
||||||
(not (file-name-quoted-p filename))
|
(not (file-name-quoted-p filename))
|
||||||
(string-match "[[*?]" filename))
|
(string-match "[[*?]" filename))
|
||||||
(file-exists-p filename))
|
(file-exists-p filename))
|
||||||
(error "%s does not exist" filename))
|
(error "%s does not exist" filename))
|
||||||
(let ((value (funcall fun filename wildcards)))
|
(find-file-with-threads filename async
|
||||||
(mapc (lambda (b) (with-current-buffer b (read-only-mode 1)))
|
(let ((value (funcall fun filename wildcards)))
|
||||||
(if (listp value) value (list value)))
|
(mapc (lambda (b) (with-current-buffer b (read-only-mode 1)))
|
||||||
value))
|
(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.
|
"Edit file FILENAME but don't allow changes.
|
||||||
Like \\[find-file], but marks buffer as read-only.
|
Like \\[find-file], but marks buffer as read-only.
|
||||||
Use \\[read-only-mode] to permit editing."
|
Use \\[read-only-mode] to permit editing."
|
||||||
(interactive
|
(interactive
|
||||||
(find-file-read-args "Find file read-only: "
|
(find-file-read-args "Find file read-only: "
|
||||||
(confirm-nonexistent-file-or-buffer)))
|
(confirm-nonexistent-file-or-buffer) t))
|
||||||
(find-file--read-only #'find-file filename wildcards))
|
(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.
|
"Edit file FILENAME in another window but don't allow changes.
|
||||||
Like \\[find-file-other-window], but marks buffer as read-only.
|
Like \\[find-file-other-window], but marks buffer as read-only.
|
||||||
Use \\[read-only-mode] to permit editing."
|
Use \\[read-only-mode] to permit editing."
|
||||||
(interactive
|
(interactive
|
||||||
(find-file-read-args "Find file read-only other window: "
|
(find-file-read-args "Find file read-only other window: "
|
||||||
(confirm-nonexistent-file-or-buffer)))
|
(confirm-nonexistent-file-or-buffer) t))
|
||||||
(find-file--read-only #'find-file-other-window filename wildcards))
|
(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.
|
"Edit file FILENAME in another frame but don't allow changes.
|
||||||
Like \\[find-file-other-frame], but marks buffer as read-only.
|
Like \\[find-file-other-frame], but marks buffer as read-only.
|
||||||
Use \\[read-only-mode] to permit editing."
|
Use \\[read-only-mode] to permit editing."
|
||||||
(interactive
|
(interactive
|
||||||
(find-file-read-args "Find file read-only other frame: "
|
(find-file-read-args "Find file read-only other frame: "
|
||||||
(confirm-nonexistent-file-or-buffer)))
|
(confirm-nonexistent-file-or-buffer) t))
|
||||||
(find-file--read-only #'find-file-other-frame filename wildcards))
|
(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.
|
"Find file FILENAME as a replacement for the file in the next window.
|
||||||
This command does not select that window.
|
This command does not select that window.
|
||||||
|
|
||||||
See \\[find-file] for the possible forms of the FILENAME argument.
|
See \\[find-file] for the possible forms of the FILENAME argument.
|
||||||
|
|
||||||
Interactively, or if WILDCARDS is non-nil in a call from Lisp,
|
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
|
(interactive
|
||||||
(save-selected-window
|
(save-selected-window
|
||||||
(other-window 1)
|
(other-window 1)
|
||||||
|
|
@ -1956,12 +2007,15 @@ expand wildcards (if any) and replace the file with multiple files."
|
||||||
(list (read-file-name
|
(list (read-file-name
|
||||||
"Find alternate file: " file-dir nil
|
"Find alternate file: " file-dir nil
|
||||||
(confirm-nonexistent-file-or-buffer) file-name)
|
(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)
|
(if (one-window-p)
|
||||||
(find-file-other-window filename wildcards)
|
(find-file-other-window filename wildcards async)
|
||||||
(save-selected-window
|
(save-selected-window
|
||||||
(other-window 1)
|
(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.
|
;; Defined and used in buffer.c, but not as a DEFVAR_LISP.
|
||||||
(defvar kill-buffer-hook nil
|
(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
|
Note: Be careful with let-binding this hook considering it is
|
||||||
frequently used for cleanup.")
|
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.
|
"Find file FILENAME, select its buffer, kill previous buffer.
|
||||||
If the current buffer now contains an empty file that you just visited
|
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.
|
\(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,
|
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.
|
||||||
|
|
||||||
If the current buffer is an indirect buffer, or the base buffer
|
If the current buffer is an indirect buffer, or the base buffer
|
||||||
for one or more indirect buffers, the other buffer(s) are not
|
for one or more indirect buffers, the other buffer(s) are not
|
||||||
killed."
|
killed."
|
||||||
|
|
@ -1999,7 +2059,10 @@ killed."
|
||||||
(list (read-file-name
|
(list (read-file-name
|
||||||
"Find alternate file: " file-dir nil
|
"Find alternate file: " file-dir nil
|
||||||
(confirm-nonexistent-file-or-buffer) file-name)
|
(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)
|
(unless (run-hook-with-args-until-failure 'kill-buffer-query-functions)
|
||||||
(user-error "Aborted"))
|
(user-error "Aborted"))
|
||||||
(and (buffer-modified-p) buffer-file-name
|
(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
|
;; Don't use `find-file' because it may end up using another window
|
||||||
;; in some corner cases, e.g. when the selected window is
|
;; in some corner cases, e.g. when the selected window is
|
||||||
;; softly-dedicated.
|
;; 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))))
|
(switch-to-buffer (if (consp newbuf) (car newbuf) newbuf))))
|
||||||
(when (eq obuf (current-buffer))
|
(when (eq obuf (current-buffer))
|
||||||
;; This executes if find-file gets an error
|
;; This executes if find-file gets an error
|
||||||
|
|
@ -2199,6 +2262,15 @@ suppresses this warning."
|
||||||
:version "21.1"
|
:version "21.1"
|
||||||
:type 'boolean)
|
: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
|
(defcustom large-file-warning-threshold 10000000
|
||||||
"Maximum size of file above which a confirmation is requested.
|
"Maximum size of file above which a confirmation is requested.
|
||||||
When nil, never request confirmation."
|
When nil, never request confirmation."
|
||||||
|
|
@ -2332,7 +2404,7 @@ be visible in the echo area."
|
||||||
(apply #'message format args)
|
(apply #'message format args)
|
||||||
(when save-silently (message nil)))
|
(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.
|
"Read file FILENAME into a buffer and return the buffer.
|
||||||
If a buffer exists visiting FILENAME, return that one, but
|
If a buffer exists visiting FILENAME, return that one, but
|
||||||
verify that the file has not changed since visited or saved.
|
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
|
Optional fourth arg WILDCARDS non-nil means do wildcard processing
|
||||||
and visit all the matching files. When wildcards are actually
|
and visit all the matching files. When wildcards are actually
|
||||||
used and expanded, return a list of buffers that are visiting
|
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
|
(setq filename
|
||||||
(abbreviate-file-name
|
(abbreviate-file-name
|
||||||
(expand-file-name filename)))
|
(expand-file-name filename)))
|
||||||
|
|
@ -2361,10 +2437,32 @@ the various files."
|
||||||
(let ((files (condition-case nil
|
(let ((files (condition-case nil
|
||||||
(file-expand-wildcards filename t)
|
(file-expand-wildcards filename t)
|
||||||
(error (list filename))))
|
(error (list filename))))
|
||||||
(find-file-wildcards nil))
|
(find-file-wildcards nil)
|
||||||
|
threads)
|
||||||
(if (null files)
|
(if (null files)
|
||||||
(find-file-noselect filename)
|
(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))
|
(let* ((buf (get-file-buffer filename))
|
||||||
(truename (abbreviate-file-name (file-truename filename)))
|
(truename (abbreviate-file-name (file-truename filename)))
|
||||||
(attributes (file-attributes truename))
|
(attributes (file-attributes truename))
|
||||||
|
|
@ -2650,7 +2748,7 @@ This has the `permanent-local' property, which takes effect if you
|
||||||
make the variable buffer-local.")
|
make the variable buffer-local.")
|
||||||
(put 'find-file-literally 'permanent-local t)
|
(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.
|
"Visit file FILENAME with no conversion of any kind.
|
||||||
Format conversion and character code conversion are both disabled,
|
Format conversion and character code conversion are both disabled,
|
||||||
and multibyte characters are disabled in the resulting buffer.
|
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,
|
If Emacs already has a buffer that is visiting the file,
|
||||||
this command asks you whether to visit it literally instead.
|
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
|
In non-interactive use, the value is the buffer where the file is
|
||||||
visited literally. If the file was visited in a buffer before
|
visited literally. If the file was visited in a buffer before
|
||||||
this command was invoked, it will reuse the existing buffer,
|
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
|
contents literally, you should create a temporary buffer and then read
|
||||||
the file contents into it using `insert-file-contents-literally'."
|
the file contents into it using `insert-file-contents-literally'."
|
||||||
(interactive
|
(interactive
|
||||||
(list (read-file-name
|
(find-file-read-args "Find file literally: "
|
||||||
"Find file literally: " nil default-directory
|
(confirm-nonexistent-file-or-buffer)))
|
||||||
(confirm-nonexistent-file-or-buffer))))
|
(find-file-with-threads filename async
|
||||||
(switch-to-buffer (find-file-noselect filename nil t)))
|
(switch-to-buffer (find-file-noselect filename nil t nil async))))
|
||||||
|
|
||||||
(defun after-find-file (&optional error warn noauto
|
(defun after-find-file (&optional error warn noauto
|
||||||
_after-find-file-from-revert-buffer
|
_after-find-file-from-revert-buffer
|
||||||
|
|
|
||||||
|
|
@ -531,7 +531,9 @@ PROPERTIES is a list of file properties (strings)."
|
||||||
(prin1-to-string key))
|
(prin1-to-string key))
|
||||||
(if (hash-table-p value)
|
(if (hash-table-p value)
|
||||||
(tramp-cache-print 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 (prin1-to-string value))
|
||||||
(prin1-to-string value))))))
|
(prin1-to-string value))))))
|
||||||
(setq result (if result (concat result " " tmp) tmp))))
|
(setq result (if result (concat result " " tmp) tmp))))
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,32 @@ Add the extension of F, if existing."
|
||||||
tramp-temp-name-prefix tramp-compat-temporary-file-directory)
|
tramp-temp-name-prefix tramp-compat-temporary-file-directory)
|
||||||
dir-flag (file-name-extension f t)))
|
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
|
;; `file-modes', `set-file-modes' and `set-file-times' got argument
|
||||||
;; FLAG in Emacs 28.1.
|
;; FLAG in Emacs 28.1.
|
||||||
(defalias 'tramp-compat-file-modes
|
(defalias 'tramp-compat-file-modes
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
(require 'tramp)
|
(require 'tramp)
|
||||||
|
|
||||||
;; Pacify byte-compiler.
|
;; Pacify byte-compiler.
|
||||||
|
(declare-function ange-ftp-ftp-process-buffer "ange-ftp")
|
||||||
(defvar ange-ftp-ftp-name-arg)
|
(defvar ange-ftp-ftp-name-arg)
|
||||||
(defvar ange-ftp-ftp-name-res)
|
(defvar ange-ftp-ftp-name-res)
|
||||||
(defvar ange-ftp-name-format)
|
(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
|
;; "ftp" method is used in the Tramp file name. So we unset
|
||||||
;; those values.
|
;; those values.
|
||||||
(ange-ftp-ftp-name-arg "")
|
(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
|
(cond
|
||||||
;; If argument is a symlink, `file-directory-p' and
|
;; If argument is a symlink, `file-directory-p' and
|
||||||
;; `file-exists-p' call the traversed file recursively. So we
|
;; `file-exists-p' call the traversed file recursively. So we
|
||||||
|
|
@ -147,9 +157,7 @@ pass to the OPERATION."
|
||||||
(lambda (operation &rest args)
|
(lambda (operation &rest args)
|
||||||
(tramp-archive-run-real-handler operation args))))
|
(tramp-archive-run-real-handler operation args))))
|
||||||
(prog1 (apply #'ange-ftp-hook-function operation args)
|
(prog1 (apply #'ange-ftp-hook-function operation args)
|
||||||
(let ((v (tramp-dissect-file-name (car args) t)))
|
(tramp-set-connection-property v "started" t))))
|
||||||
(setf (tramp-file-name-method v) tramp-ftp-method)
|
|
||||||
(tramp-set-connection-property v "started" t)))))
|
|
||||||
|
|
||||||
;; If the second argument of `copy-file' or `rename-file' is a
|
;; If the second argument of `copy-file' or `rename-file' is a
|
||||||
;; remote file name but via FTP, ange-ftp doesn't check this.
|
;; remote file name but via FTP, ange-ftp doesn't check this.
|
||||||
|
|
|
||||||
|
|
@ -2107,6 +2107,10 @@ ARGUMENTS to actually emit the message (if applicable)."
|
||||||
(insert "\n"))
|
(insert "\n"))
|
||||||
;; Timestamp.
|
;; Timestamp.
|
||||||
(insert (format-time-string "%T.%6N "))
|
(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
|
;; Calling Tramp function. We suppress compat and trace
|
||||||
;; functions from being displayed.
|
;; functions from being displayed.
|
||||||
(let ((btn 1) btf fn)
|
(let ((btn 1) btf fn)
|
||||||
|
|
@ -2684,11 +2688,23 @@ Must be handled by the callers."
|
||||||
res (cdr elt))))
|
res (cdr elt))))
|
||||||
res)))
|
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.
|
;; Main function.
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun tramp-file-name-handler (operation &rest args)
|
(defun tramp-file-name-handler (operation &rest args)
|
||||||
"Invoke Tramp file name handler for OPERATION and 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))
|
(let ((filename (apply #'tramp-file-name-for-operation operation args))
|
||||||
;; `file-remote-p' is called for everything, even for symbolic
|
;; `file-remote-p' is called for everything, even for symbolic
|
||||||
;; links which look remote. We don't want to get an error.
|
;; 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
|
(save-match-data
|
||||||
(setq filename (tramp-replace-environment-variables filename))
|
(setq filename (tramp-replace-environment-variables filename))
|
||||||
(with-parsed-tramp-file-name filename nil
|
(with-parsed-tramp-file-name filename nil
|
||||||
(let ((current-connection tramp-current-connection)
|
;; Give other threads a chance.
|
||||||
(foreign
|
(tramp-compat-thread-yield)
|
||||||
(tramp-find-foreign-file-name-handler v operation))
|
;; The mutex allows concurrent run of operations. It
|
||||||
(signal-hook-function #'tramp-signal-hook-function)
|
;; guarantees, that the threads are not mixed.
|
||||||
result)
|
(tramp-compat-with-mutex (tramp-get-mutex v)
|
||||||
;; Set `tramp-current-connection'.
|
;; Run only when Emacs is idle.
|
||||||
(unless
|
(tramp-compat-funcall 'check-idle-thread)
|
||||||
(tramp-file-name-equal-p v (car tramp-current-connection))
|
(let ((current-connection tramp-current-connection)
|
||||||
(setq tramp-current-connection (list v)))
|
(foreign (tramp-find-foreign-file-name-handler v operation))
|
||||||
|
(signal-hook-function #'tramp-signal-hook-function)
|
||||||
;; Call the backend function.
|
result)
|
||||||
(unwind-protect
|
;; Set `tramp-current-connection'.
|
||||||
(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'.
|
|
||||||
(unless
|
(unless
|
||||||
(tramp-file-name-equal-p
|
(tramp-file-name-equal-p v (car tramp-current-connection))
|
||||||
(car current-connection) (car tramp-current-connection))
|
(setq tramp-current-connection (list v)))
|
||||||
(setq tramp-current-connection current-connection))))))
|
|
||||||
|
;; 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,
|
;; When `tramp-mode' is not enabled, or the file name is quoted,
|
||||||
;; we don't do anything.
|
;; we don't do anything.
|
||||||
|
|
|
||||||
|
|
@ -5449,6 +5449,53 @@ These commands include \\[set-mark-command] and \\[start-kbd-macro]."
|
||||||
(t
|
(t
|
||||||
digit))))
|
digit))))
|
||||||
(universal-argument--mode))
|
(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
|
(defvar filter-buffer-substring-functions nil
|
||||||
|
|
|
||||||
|
|
@ -774,6 +774,10 @@ current, and kill the buffer that visits the link."
|
||||||
(defun vc-default-find-file-hook (_backend)
|
(defun vc-default-find-file-hook (_backend)
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
|
;;;###autoload
|
||||||
|
(defvar vc-mutex (make-mutex "vc")
|
||||||
|
"Mutex for vc operations.")
|
||||||
|
|
||||||
(defun vc-refresh-state ()
|
(defun vc-refresh-state ()
|
||||||
"Refresh the VC state of the current buffer's file.
|
"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.
|
also supports switching a back-end or removing the file from VC.
|
||||||
In the latter case, VC mode is deactivated for this buffer."
|
In the latter case, VC mode is deactivated for this buffer."
|
||||||
(interactive)
|
(interactive)
|
||||||
;; Recompute whether file is version controlled,
|
;; Recompute whether file is version controlled, if user has killed
|
||||||
;; if user has killed the buffer and revisited.
|
;; the buffer and revisited.
|
||||||
(when vc-mode
|
(when vc-mode
|
||||||
(setq vc-mode nil))
|
(setq vc-mode nil))
|
||||||
(when buffer-file-name
|
(when buffer-file-name
|
||||||
(vc-file-clearprops buffer-file-name)
|
;; Run it asynchronously.
|
||||||
;; FIXME: Why use a hook? Why pass it buffer-file-name?
|
(make-thread
|
||||||
(add-hook 'vc-mode-line-hook #'vc-mode-line nil t)
|
(lambda ()
|
||||||
(let (backend)
|
;; Wait, until the file visiting function tells us so.
|
||||||
(cond
|
(with-mutex vc-mutex)
|
||||||
((setq backend (with-demoted-errors "VC refresh error: %S"
|
(vc-file-clearprops buffer-file-name)
|
||||||
(vc-backend buffer-file-name)))
|
(vc-file-clearprops buffer-file-name)
|
||||||
;; Let the backend setup any buffer-local things he needs.
|
;; FIXME: Why use a hook? Why pass it buffer-file-name?
|
||||||
(vc-call-backend backend 'find-file-hook)
|
(add-hook 'vc-mode-line-hook #'vc-mode-line nil t)
|
||||||
;; Compute the state and put it in the mode line.
|
(let (backend)
|
||||||
(vc-mode-line buffer-file-name backend)
|
(cond
|
||||||
(unless vc-make-backup-files
|
((setq backend (with-demoted-errors "VC refresh error: %S"
|
||||||
;; Use this variable, not make-backup-files,
|
(vc-backend buffer-file-name)))
|
||||||
;; because this is for things that depend on the file name.
|
;; Let the backend setup any buffer-local things he needs.
|
||||||
(setq-local backup-inhibited t)))
|
(vc-call-backend backend 'find-file-hook)
|
||||||
((let* ((truename (and buffer-file-truename
|
;; Compute the state and put it in the mode line.
|
||||||
(expand-file-name buffer-file-truename)))
|
(vc-mode-line buffer-file-name backend)
|
||||||
(link-type (and truename
|
(unless vc-make-backup-files
|
||||||
(not (equal buffer-file-name truename))
|
;; Use this variable, not make-backup-files, because this
|
||||||
(vc-backend truename))))
|
;; is for things that depend on the file name.
|
||||||
(cond ((not link-type) nil) ;Nothing to do.
|
(setq-local backup-inhibited t)))
|
||||||
((eq vc-follow-symlinks nil)
|
((let* ((truename (and buffer-file-truename
|
||||||
(message
|
(expand-file-name buffer-file-truename)))
|
||||||
"Warning: symbolic link to %s-controlled source file" link-type))
|
(link-type (and truename
|
||||||
((or (not (eq vc-follow-symlinks 'ask))
|
(not (equal buffer-file-name truename))
|
||||||
;; Assume we cannot ask, default to yes.
|
(vc-backend truename))))
|
||||||
noninteractive
|
(cond ((not link-type) nil) ;Nothing to do.
|
||||||
;; Copied from server-start. Seems like there should
|
((eq vc-follow-symlinks nil)
|
||||||
;; be a better way to ask "can we get user input?"...
|
(message
|
||||||
(and (daemonp)
|
"Warning: symbolic link to %s-controlled source file"
|
||||||
(null (cdr (frame-list)))
|
link-type))
|
||||||
(eq (selected-frame) terminal-frame))
|
((or (not (eq vc-follow-symlinks 'ask))
|
||||||
;; If we already visited this file by following
|
;; Assume we cannot ask, default to yes.
|
||||||
;; the link, don't ask again if we try to visit
|
noninteractive
|
||||||
;; it again. GUD does that, and repeated questions
|
;; Copied from server-start. Seems like there
|
||||||
;; are painful.
|
;; should be a better way to ask "can we get
|
||||||
(get-file-buffer
|
;; user input?"...
|
||||||
(abbreviate-file-name
|
(and (daemonp)
|
||||||
(file-chase-links buffer-file-name))))
|
(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)
|
(vc-follow-link)
|
||||||
(message "Followed link to %s" buffer-file-name)
|
(message "Followed link to %s" buffer-file-name)
|
||||||
(vc-refresh-state))
|
(vc-refresh-state))
|
||||||
(t
|
(t
|
||||||
(if (yes-or-no-p (format
|
(if (yes-or-no-p
|
||||||
"Symbolic link to %s-controlled source file; follow link? " link-type))
|
(format
|
||||||
(progn (vc-follow-link)
|
(concat
|
||||||
(message "Followed link to %s" buffer-file-name)
|
"Symbolic link to %s-controlled source file; "
|
||||||
(vc-refresh-state))
|
"follow link? ")
|
||||||
(message
|
link-type))
|
||||||
"Warning: editing through the link bypasses version control")
|
(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)
|
(add-hook 'find-file-hook #'vc-refresh-state)
|
||||||
(define-obsolete-function-alias 'vc-find-file-hook #'vc-refresh-state "25.1")
|
(define-obsolete-function-alias 'vc-find-file-hook #'vc-refresh-state "25.1")
|
||||||
|
|
|
||||||
|
|
@ -7681,8 +7681,114 @@ process sentinels. They shall not disturb each other."
|
||||||
(let ((auth-sources `(,netrc-file)))
|
(let ((auth-sources `(,netrc-file)))
|
||||||
(should (file-exists-p ert-remote-temporary-file-directory)))))))))
|
(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.
|
;; This test is inspired by Bug#29163.
|
||||||
(ert-deftest tramp-test48-auto-load ()
|
(ert-deftest tramp-test49-auto-load ()
|
||||||
"Check that Tramp autoloads properly."
|
"Check that Tramp autoloads properly."
|
||||||
;; If we use another syntax but `default', Tramp is already loaded
|
;; If we use another syntax but `default', Tramp is already loaded
|
||||||
;; due to the `tramp-change-syntax' call.
|
;; 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 ")
|
(mapconcat #'shell-quote-argument load-path " -L ")
|
||||||
(shell-quote-argument code)))))))
|
(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."
|
"Check that Tramp is loaded lazily, only when needed."
|
||||||
;; Tramp is neither loaded at Emacs startup, nor when completing a
|
;; Tramp is neither loaded at Emacs startup, nor when completing a
|
||||||
;; non-Tramp file name like "/foo". Completing a Tramp-alike file
|
;; 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 ")
|
(mapconcat #'shell-quote-argument load-path " -L ")
|
||||||
(shell-quote-argument (format code tm)))))))))
|
(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."
|
"Check that Tramp does not fail due to recursive load."
|
||||||
(skip-unless (tramp--test-enabled))
|
(skip-unless (tramp--test-enabled))
|
||||||
|
|
||||||
|
|
@ -7761,7 +7867,7 @@ process sentinels. They shall not disturb each other."
|
||||||
(mapconcat #'shell-quote-argument load-path " -L ")
|
(mapconcat #'shell-quote-argument load-path " -L ")
|
||||||
(shell-quote-argument code))))))))
|
(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'."
|
"Check that Tramp autoloads its packages with remote `load-path'."
|
||||||
;; `tramp-cleanup-all-connections' is autoloaded from tramp-cmds.el.
|
;; `tramp-cleanup-all-connections' is autoloaded from tramp-cmds.el.
|
||||||
;; It shall still work, when a remote file name is in the
|
;; 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 ")
|
(mapconcat #'shell-quote-argument load-path " -L ")
|
||||||
(shell-quote-argument code)))))))
|
(shell-quote-argument code)))))))
|
||||||
|
|
||||||
(ert-deftest tramp-test49-unload ()
|
(ert-deftest tramp-test50-unload ()
|
||||||
"Check that Tramp and its subpackages unload completely.
|
"Check that Tramp and its subpackages unload completely.
|
||||||
Since it unloads Tramp, it shall be the last test to run."
|
Since it unloads Tramp, it shall be the last test to run."
|
||||||
:tags '(:expensive-test)
|
: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.
|
;; async processes. Check, why they don't run stable.
|
||||||
;; * Check, why direct async processes do not work for
|
;; * Check, why direct async processes do not work for
|
||||||
;; `tramp-test45-asynchronous-requests'.
|
;; `tramp-test45-asynchronous-requests'.
|
||||||
|
;; * Fix `tramp-test48-threads'.
|
||||||
|
|
||||||
(provide 'tramp-tests)
|
(provide 'tramp-tests)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue