File notifications: Support renaming over directory boundaries
* lisp/filenotify.el (file-notify-handle-event): (file-notify--pending-event): Adapt docstring. (file-notify--descriptor, file-notify-callback): Reimplement in order to support renaming over directory boundaries. (file-notify-add-watch): Adapt `file-notify--descriptor' call. * doc/lispref/os.texi (File Notifications): Remove limitation of file renaming to the same directory.
This commit is contained in:
parent
29b0e0bb2c
commit
dbdc459a48
2 changed files with 55 additions and 79 deletions
|
@ -2675,32 +2675,14 @@ being reported. For example:
|
|||
@end example
|
||||
|
||||
Whether the action @code{renamed} is returned, depends on the used
|
||||
watch library. It can be expected, when a directory is watched, and
|
||||
both @var{file} and @var{file1} belong to this directory. Otherwise,
|
||||
the actions @code{deleted} and @code{created} could be returned in a
|
||||
random order.
|
||||
watch library. Otherwise, the actions @code{deleted} and
|
||||
@code{created} could be returned in a random order.
|
||||
|
||||
@example
|
||||
@group
|
||||
(rename-file "/tmp/foo" "/tmp/bla")
|
||||
@result{} Event (35025468 renamed "/tmp/foo" "/tmp/bla")
|
||||
@end group
|
||||
|
||||
@group
|
||||
(file-notify-add-watch
|
||||
"/var/tmp" '(change attribute-change) 'my-notify-callback)
|
||||
@result{} 35025504
|
||||
@end group
|
||||
|
||||
@group
|
||||
(rename-file "/tmp/bla" "/var/tmp/bla")
|
||||
@result{} ;; gfilenotify
|
||||
Event (35025468 renamed "/tmp/bla" "/var/tmp/bla")
|
||||
|
||||
@result{} ;; inotify
|
||||
Event (35025504 created "/var/tmp/bla")
|
||||
Event (35025468 deleted "/tmp/bla")
|
||||
@end group
|
||||
@end example
|
||||
@end defun
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ different files from the same directory are watched.")
|
|||
"Handle file system monitoring event.
|
||||
If EVENT is a filewatch event, call its callback. It has the format
|
||||
|
||||
\(file-notify (DESCRIPTOR ACTIONS FILE COOKIE) CALLBACK)
|
||||
\(file-notify (DESCRIPTOR ACTIONS FILE [FILE1-OR-COOKIE]) CALLBACK)
|
||||
|
||||
Otherwise, signal a `file-notify-error'."
|
||||
(interactive "e")
|
||||
|
@ -64,10 +64,10 @@ Otherwise, signal a `file-notify-error'."
|
|||
(signal 'file-notify-error
|
||||
(cons "Not a valid file-notify event" event))))
|
||||
|
||||
(defvar file-notify--pending-events nil
|
||||
"List of pending file notification events for a future `renamed' action.
|
||||
The entries are a list (DESCRIPTOR ACTION FILE COOKIE). ACTION
|
||||
is either `moved-from' or `renamed-from'.")
|
||||
;; Needed for `inotify' and `w32notify'. In the latter case, COOKIE is nil.
|
||||
(defvar file-notify--pending-event nil
|
||||
"A pending file notification events for a future `renamed' action.
|
||||
It is a form ((DESCRIPTOR ACTION FILE [FILE1-OR-COOKIE]) CALLBACK).")
|
||||
|
||||
(defun file-notify--event-file-name (event)
|
||||
"Return file name of file notification event, or nil."
|
||||
|
@ -92,26 +92,26 @@ This is available in case a file has been moved."
|
|||
;; `inotify' returns the same descriptor when the file (directory)
|
||||
;; uses the same inode. We want to distinguish, and apply a virtual
|
||||
;; descriptor which make the difference.
|
||||
(defun file-notify--descriptor (descriptor file)
|
||||
(defun file-notify--descriptor (descriptor)
|
||||
"Return the descriptor to be used in `file-notify-*-watch'.
|
||||
For `gfilenotify' and `w32notify' it is the same descriptor as
|
||||
used in the low-level file notification package."
|
||||
(if (and (natnump descriptor) (eq file-notify--library 'inotify))
|
||||
(cons descriptor file)
|
||||
(cons descriptor
|
||||
(car (cadr (gethash descriptor file-notify-descriptors))))
|
||||
descriptor))
|
||||
|
||||
;; The callback function used to map between specific flags of the
|
||||
;; respective file notifications, and the ones we return.
|
||||
(defun file-notify-callback (event)
|
||||
"Handle an EVENT returned from file notification.
|
||||
EVENT is the cdr of the event in `file-notify-handle-event'
|
||||
\(DESCRIPTOR ACTIONS FILE COOKIE)."
|
||||
EVENT is the cadr of the event in `file-notify-handle-event'
|
||||
\(DESCRIPTOR ACTIONS FILE [FILE1-OR-COOKIE])."
|
||||
(let* ((desc (car event))
|
||||
(registered (gethash desc file-notify-descriptors))
|
||||
(pending-event (assoc desc file-notify--pending-events))
|
||||
(actions (nth 1 event))
|
||||
(file (file-notify--event-file-name event))
|
||||
file1 callback)
|
||||
file1 callback pending-event)
|
||||
|
||||
;; Make actions a list.
|
||||
(unless (consp actions) (setq actions (cons actions nil)))
|
||||
|
@ -129,22 +129,23 @@ EVENT is the cdr of the event in `file-notify-handle-event'
|
|||
(dolist (action actions)
|
||||
|
||||
;; Send pending event, if it doesn't match.
|
||||
(when (and pending-event
|
||||
(when (and file-notify--pending-event
|
||||
;; The cookie doesn't match.
|
||||
(not (eq (file-notify--event-cookie pending-event)
|
||||
(not (eq (file-notify--event-cookie
|
||||
(car file-notify--pending-event))
|
||||
(file-notify--event-cookie event)))
|
||||
(or
|
||||
;; inotify.
|
||||
(and (eq (nth 1 pending-event) 'moved-from)
|
||||
(and (eq (nth 1 (car file-notify--pending-event))
|
||||
'moved-from)
|
||||
(not (eq action 'moved-to)))
|
||||
;; w32notify.
|
||||
(and (eq (nth 1 pending-event) 'renamed-from)
|
||||
(and (eq (nth 1 (car file-notify--pending-event))
|
||||
'renamed-from)
|
||||
(not (eq action 'renamed-to)))))
|
||||
(funcall callback
|
||||
(list desc 'deleted
|
||||
(file-notify--event-file-name pending-event)))
|
||||
(setq file-notify--pending-events
|
||||
(delete pending-event file-notify--pending-events)))
|
||||
(setq pending-event file-notify--pending-event
|
||||
file-notify--pending-event nil)
|
||||
(setcar (cdar pending-event) 'deleted))
|
||||
|
||||
;; Map action. We ignore all events which cannot be mapped.
|
||||
(setq action
|
||||
|
@ -156,46 +157,42 @@ EVENT is the cdr of the event in `file-notify-handle-event'
|
|||
(setq file1 (file-notify--event-file1-name event))
|
||||
'renamed)
|
||||
|
||||
;; inotify.
|
||||
;; inotify, w32notify.
|
||||
((eq action 'attrib) 'attribute-changed)
|
||||
((eq action 'create) 'created)
|
||||
((eq action 'modify) 'changed)
|
||||
((memq action '(delete 'delete-self move-self)) 'deleted)
|
||||
((memq action '(create added)) 'created)
|
||||
((memq action '(modify modified)) 'changed)
|
||||
((memq action '(delete 'delete-self move-self removed)) 'deleted)
|
||||
;; Make the event pending.
|
||||
((eq action 'moved-from)
|
||||
(add-to-list 'file-notify--pending-events
|
||||
(list desc action file
|
||||
(file-notify--event-cookie event)))
|
||||
((memq action '(moved-from renamed-from))
|
||||
(setq file-notify--pending-event
|
||||
`((,desc ,action ,file ,(file-notify--event-cookie event))
|
||||
,callback))
|
||||
nil)
|
||||
;; Look for pending event.
|
||||
((eq action 'moved-to)
|
||||
(if (null pending-event)
|
||||
((memq action '(moved-to renamed-to))
|
||||
(if (null file-notify--pending-event)
|
||||
'created
|
||||
(setq file1 file
|
||||
file (file-notify--event-file-name pending-event)
|
||||
file-notify--pending-events
|
||||
(delete pending-event file-notify--pending-events))
|
||||
'renamed))
|
||||
file (file-notify--event-file-name
|
||||
(car file-notify--pending-event)))
|
||||
;; If the source is handled by another watch, we
|
||||
;; must fire the rename event there as well.
|
||||
(when (not (eq (file-notify--descriptor desc)
|
||||
(file-notify--descriptor
|
||||
(caar file-notify--pending-event))))
|
||||
(setq pending-event
|
||||
`((,(caar file-notify--pending-event)
|
||||
renamed ,file ,file1)
|
||||
,(cadr file-notify--pending-event))))
|
||||
(setq file-notify--pending-event nil)
|
||||
'renamed))))
|
||||
|
||||
;; w32notify.
|
||||
((eq action 'added) 'created)
|
||||
((eq action 'modified) 'changed)
|
||||
((eq action 'removed) 'deleted)
|
||||
;; Make the event pending.
|
||||
((eq action 'renamed-from)
|
||||
(add-to-list 'file-notify--pending-events
|
||||
(list desc action file
|
||||
(file-notify--event-cookie event)))
|
||||
nil)
|
||||
;; Look for pending event.
|
||||
((eq action 'renamed-to)
|
||||
(if (null pending-event)
|
||||
'created
|
||||
(setq file1 file
|
||||
file (file-notify--event-file-name pending-event)
|
||||
file-notify--pending-events
|
||||
(delete pending-event file-notify--pending-events))
|
||||
'renamed))))
|
||||
;; Apply pending callback.
|
||||
(when pending-event
|
||||
(setcar
|
||||
(car pending-event) (file-notify--descriptor (caar pending-event)))
|
||||
(funcall (cadr pending-event) (car pending-event))
|
||||
(setq pending-event nil))
|
||||
|
||||
;; Apply callback.
|
||||
(when (and action
|
||||
|
@ -213,12 +210,10 @@ EVENT is the cdr of the event in `file-notify-handle-event'
|
|||
(if file1
|
||||
(funcall
|
||||
callback
|
||||
`(,(file-notify--descriptor desc (nth 0 entry))
|
||||
,action ,file ,file1))
|
||||
`(,(file-notify--descriptor desc) ,action ,file ,file1))
|
||||
(funcall
|
||||
callback
|
||||
`(,(file-notify--descriptor desc (nth 0 entry))
|
||||
,action ,file))))))))
|
||||
`(,(file-notify--descriptor desc) ,action ,file))))))))
|
||||
|
||||
;; `gfilenotify' and `w32notify' return a unique descriptor for every
|
||||
;; `file-notify-add-watch', while `inotify' returns a unique
|
||||
|
@ -325,8 +320,7 @@ FILE is the name of the file whose event is being reported."
|
|||
file-notify-descriptors)
|
||||
|
||||
;; Return descriptor.
|
||||
(file-notify--descriptor
|
||||
desc (unless (file-directory-p file) (file-name-nondirectory file)))))
|
||||
(file-notify--descriptor desc)))
|
||||
|
||||
(defun file-notify-rm-watch (descriptor)
|
||||
"Remove an existing watch specified by its DESCRIPTOR.
|
||||
|
|
Loading…
Add table
Reference in a new issue