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
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
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
|
||||
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
|
||||
|
||||
+++
|
||||
|
|
|
|||
238
lisp/files.el
238
lisp/files.el
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue