diff options
| author | Michael Albinus | 2015-09-06 14:21:56 +0200 |
|---|---|---|
| committer | Michael Albinus | 2015-09-06 14:21:56 +0200 |
| commit | dbdc459a48091f5953faf14bcaaa7e6d37fbf024 (patch) | |
| tree | 5ea667562039d35f4e6def4aec933ca0149c3988 | |
| parent | 29b0e0bb2c4e09465b97497ba96886e90a64ad71 (diff) | |
| download | emacs-dbdc459a48091f5953faf14bcaaa7e6d37fbf024.tar.gz emacs-dbdc459a48091f5953faf14bcaaa7e6d37fbf024.zip | |
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.
| -rw-r--r-- | doc/lispref/os.texi | 22 | ||||
| -rw-r--r-- | lisp/filenotify.el | 114 |
2 files changed, 56 insertions, 80 deletions
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi index 8d7177dc1d5..f7d4117673a 100644 --- a/doc/lispref/os.texi +++ b/doc/lispref/os.texi | |||
| @@ -2675,32 +2675,14 @@ being reported. For example: | |||
| 2675 | @end example | 2675 | @end example |
| 2676 | 2676 | ||
| 2677 | Whether the action @code{renamed} is returned, depends on the used | 2677 | Whether the action @code{renamed} is returned, depends on the used |
| 2678 | watch library. It can be expected, when a directory is watched, and | 2678 | watch library. Otherwise, the actions @code{deleted} and |
| 2679 | both @var{file} and @var{file1} belong to this directory. Otherwise, | 2679 | @code{created} could be returned in a random order. |
| 2680 | the actions @code{deleted} and @code{created} could be returned in a | ||
| 2681 | random order. | ||
| 2682 | 2680 | ||
| 2683 | @example | 2681 | @example |
| 2684 | @group | 2682 | @group |
| 2685 | (rename-file "/tmp/foo" "/tmp/bla") | 2683 | (rename-file "/tmp/foo" "/tmp/bla") |
| 2686 | @result{} Event (35025468 renamed "/tmp/foo" "/tmp/bla") | 2684 | @result{} Event (35025468 renamed "/tmp/foo" "/tmp/bla") |
| 2687 | @end group | 2685 | @end group |
| 2688 | |||
| 2689 | @group | ||
| 2690 | (file-notify-add-watch | ||
| 2691 | "/var/tmp" '(change attribute-change) 'my-notify-callback) | ||
| 2692 | @result{} 35025504 | ||
| 2693 | @end group | ||
| 2694 | |||
| 2695 | @group | ||
| 2696 | (rename-file "/tmp/bla" "/var/tmp/bla") | ||
| 2697 | @result{} ;; gfilenotify | ||
| 2698 | Event (35025468 renamed "/tmp/bla" "/var/tmp/bla") | ||
| 2699 | |||
| 2700 | @result{} ;; inotify | ||
| 2701 | Event (35025504 created "/var/tmp/bla") | ||
| 2702 | Event (35025468 deleted "/tmp/bla") | ||
| 2703 | @end group | ||
| 2704 | @end example | 2686 | @end example |
| 2705 | @end defun | 2687 | @end defun |
| 2706 | 2688 | ||
diff --git a/lisp/filenotify.el b/lisp/filenotify.el index c94f631dde8..5822cf0cc7e 100644 --- a/lisp/filenotify.el +++ b/lisp/filenotify.el | |||
| @@ -54,7 +54,7 @@ different files from the same directory are watched.") | |||
| 54 | "Handle file system monitoring event. | 54 | "Handle file system monitoring event. |
| 55 | If EVENT is a filewatch event, call its callback. It has the format | 55 | If EVENT is a filewatch event, call its callback. It has the format |
| 56 | 56 | ||
| 57 | \(file-notify (DESCRIPTOR ACTIONS FILE COOKIE) CALLBACK) | 57 | \(file-notify (DESCRIPTOR ACTIONS FILE [FILE1-OR-COOKIE]) CALLBACK) |
| 58 | 58 | ||
| 59 | Otherwise, signal a `file-notify-error'." | 59 | Otherwise, signal a `file-notify-error'." |
| 60 | (interactive "e") | 60 | (interactive "e") |
| @@ -64,10 +64,10 @@ Otherwise, signal a `file-notify-error'." | |||
| 64 | (signal 'file-notify-error | 64 | (signal 'file-notify-error |
| 65 | (cons "Not a valid file-notify event" event)))) | 65 | (cons "Not a valid file-notify event" event)))) |
| 66 | 66 | ||
| 67 | (defvar file-notify--pending-events nil | 67 | ;; Needed for `inotify' and `w32notify'. In the latter case, COOKIE is nil. |
| 68 | "List of pending file notification events for a future `renamed' action. | 68 | (defvar file-notify--pending-event nil |
| 69 | The entries are a list (DESCRIPTOR ACTION FILE COOKIE). ACTION | 69 | "A pending file notification events for a future `renamed' action. |
| 70 | is either `moved-from' or `renamed-from'.") | 70 | It is a form ((DESCRIPTOR ACTION FILE [FILE1-OR-COOKIE]) CALLBACK).") |
| 71 | 71 | ||
| 72 | (defun file-notify--event-file-name (event) | 72 | (defun file-notify--event-file-name (event) |
| 73 | "Return file name of file notification event, or nil." | 73 | "Return file name of file notification event, or nil." |
| @@ -92,26 +92,26 @@ This is available in case a file has been moved." | |||
| 92 | ;; `inotify' returns the same descriptor when the file (directory) | 92 | ;; `inotify' returns the same descriptor when the file (directory) |
| 93 | ;; uses the same inode. We want to distinguish, and apply a virtual | 93 | ;; uses the same inode. We want to distinguish, and apply a virtual |
| 94 | ;; descriptor which make the difference. | 94 | ;; descriptor which make the difference. |
| 95 | (defun file-notify--descriptor (descriptor file) | 95 | (defun file-notify--descriptor (descriptor) |
| 96 | "Return the descriptor to be used in `file-notify-*-watch'. | 96 | "Return the descriptor to be used in `file-notify-*-watch'. |
| 97 | For `gfilenotify' and `w32notify' it is the same descriptor as | 97 | For `gfilenotify' and `w32notify' it is the same descriptor as |
| 98 | used in the low-level file notification package." | 98 | used in the low-level file notification package." |
| 99 | (if (and (natnump descriptor) (eq file-notify--library 'inotify)) | 99 | (if (and (natnump descriptor) (eq file-notify--library 'inotify)) |
| 100 | (cons descriptor file) | 100 | (cons descriptor |
| 101 | (car (cadr (gethash descriptor file-notify-descriptors)))) | ||
| 101 | descriptor)) | 102 | descriptor)) |
| 102 | 103 | ||
| 103 | ;; The callback function used to map between specific flags of the | 104 | ;; The callback function used to map between specific flags of the |
| 104 | ;; respective file notifications, and the ones we return. | 105 | ;; respective file notifications, and the ones we return. |
| 105 | (defun file-notify-callback (event) | 106 | (defun file-notify-callback (event) |
| 106 | "Handle an EVENT returned from file notification. | 107 | "Handle an EVENT returned from file notification. |
| 107 | EVENT is the cdr of the event in `file-notify-handle-event' | 108 | EVENT is the cadr of the event in `file-notify-handle-event' |
| 108 | \(DESCRIPTOR ACTIONS FILE COOKIE)." | 109 | \(DESCRIPTOR ACTIONS FILE [FILE1-OR-COOKIE])." |
| 109 | (let* ((desc (car event)) | 110 | (let* ((desc (car event)) |
| 110 | (registered (gethash desc file-notify-descriptors)) | 111 | (registered (gethash desc file-notify-descriptors)) |
| 111 | (pending-event (assoc desc file-notify--pending-events)) | ||
| 112 | (actions (nth 1 event)) | 112 | (actions (nth 1 event)) |
| 113 | (file (file-notify--event-file-name event)) | 113 | (file (file-notify--event-file-name event)) |
| 114 | file1 callback) | 114 | file1 callback pending-event) |
| 115 | 115 | ||
| 116 | ;; Make actions a list. | 116 | ;; Make actions a list. |
| 117 | (unless (consp actions) (setq actions (cons actions nil))) | 117 | (unless (consp actions) (setq actions (cons actions nil))) |
| @@ -129,22 +129,23 @@ EVENT is the cdr of the event in `file-notify-handle-event' | |||
| 129 | (dolist (action actions) | 129 | (dolist (action actions) |
| 130 | 130 | ||
| 131 | ;; Send pending event, if it doesn't match. | 131 | ;; Send pending event, if it doesn't match. |
| 132 | (when (and pending-event | 132 | (when (and file-notify--pending-event |
| 133 | ;; The cookie doesn't match. | 133 | ;; The cookie doesn't match. |
| 134 | (not (eq (file-notify--event-cookie pending-event) | 134 | (not (eq (file-notify--event-cookie |
| 135 | (car file-notify--pending-event)) | ||
| 135 | (file-notify--event-cookie event))) | 136 | (file-notify--event-cookie event))) |
| 136 | (or | 137 | (or |
| 137 | ;; inotify. | 138 | ;; inotify. |
| 138 | (and (eq (nth 1 pending-event) 'moved-from) | 139 | (and (eq (nth 1 (car file-notify--pending-event)) |
| 140 | 'moved-from) | ||
| 139 | (not (eq action 'moved-to))) | 141 | (not (eq action 'moved-to))) |
| 140 | ;; w32notify. | 142 | ;; w32notify. |
| 141 | (and (eq (nth 1 pending-event) 'renamed-from) | 143 | (and (eq (nth 1 (car file-notify--pending-event)) |
| 144 | 'renamed-from) | ||
| 142 | (not (eq action 'renamed-to))))) | 145 | (not (eq action 'renamed-to))))) |
| 143 | (funcall callback | 146 | (setq pending-event file-notify--pending-event |
| 144 | (list desc 'deleted | 147 | file-notify--pending-event nil) |
| 145 | (file-notify--event-file-name pending-event))) | 148 | (setcar (cdar pending-event) 'deleted)) |
| 146 | (setq file-notify--pending-events | ||
| 147 | (delete pending-event file-notify--pending-events))) | ||
| 148 | 149 | ||
| 149 | ;; Map action. We ignore all events which cannot be mapped. | 150 | ;; Map action. We ignore all events which cannot be mapped. |
| 150 | (setq action | 151 | (setq action |
| @@ -156,46 +157,42 @@ EVENT is the cdr of the event in `file-notify-handle-event' | |||
| 156 | (setq file1 (file-notify--event-file1-name event)) | 157 | (setq file1 (file-notify--event-file1-name event)) |
| 157 | 'renamed) | 158 | 'renamed) |
| 158 | 159 | ||
| 159 | ;; inotify. | 160 | ;; inotify, w32notify. |
| 160 | ((eq action 'attrib) 'attribute-changed) | 161 | ((eq action 'attrib) 'attribute-changed) |
| 161 | ((eq action 'create) 'created) | 162 | ((memq action '(create added)) 'created) |
| 162 | ((eq action 'modify) 'changed) | 163 | ((memq action '(modify modified)) 'changed) |
| 163 | ((memq action '(delete 'delete-self move-self)) 'deleted) | 164 | ((memq action '(delete 'delete-self move-self removed)) 'deleted) |
| 164 | ;; Make the event pending. | 165 | ;; Make the event pending. |
| 165 | ((eq action 'moved-from) | 166 | ((memq action '(moved-from renamed-from)) |
| 166 | (add-to-list 'file-notify--pending-events | 167 | (setq file-notify--pending-event |
| 167 | (list desc action file | 168 | `((,desc ,action ,file ,(file-notify--event-cookie event)) |
| 168 | (file-notify--event-cookie event))) | 169 | ,callback)) |
| 169 | nil) | 170 | nil) |
| 170 | ;; Look for pending event. | 171 | ;; Look for pending event. |
| 171 | ((eq action 'moved-to) | 172 | ((memq action '(moved-to renamed-to)) |
| 172 | (if (null pending-event) | 173 | (if (null file-notify--pending-event) |
| 173 | 'created | 174 | 'created |
| 174 | (setq file1 file | 175 | (setq file1 file |
| 175 | file (file-notify--event-file-name pending-event) | 176 | file (file-notify--event-file-name |
| 176 | file-notify--pending-events | 177 | (car file-notify--pending-event))) |
| 177 | (delete pending-event file-notify--pending-events)) | 178 | ;; If the source is handled by another watch, we |
| 178 | 'renamed)) | 179 | ;; must fire the rename event there as well. |
| 179 | 180 | (when (not (eq (file-notify--descriptor desc) | |
| 180 | ;; w32notify. | 181 | (file-notify--descriptor |
| 181 | ((eq action 'added) 'created) | 182 | (caar file-notify--pending-event)))) |
| 182 | ((eq action 'modified) 'changed) | 183 | (setq pending-event |
| 183 | ((eq action 'removed) 'deleted) | 184 | `((,(caar file-notify--pending-event) |
| 184 | ;; Make the event pending. | 185 | renamed ,file ,file1) |
| 185 | ((eq action 'renamed-from) | 186 | ,(cadr file-notify--pending-event)))) |
| 186 | (add-to-list 'file-notify--pending-events | 187 | (setq file-notify--pending-event nil) |
| 187 | (list desc action file | 188 | 'renamed)))) |
| 188 | (file-notify--event-cookie event))) | 189 | |
| 189 | nil) | 190 | ;; Apply pending callback. |
| 190 | ;; Look for pending event. | 191 | (when pending-event |
| 191 | ((eq action 'renamed-to) | 192 | (setcar |
| 192 | (if (null pending-event) | 193 | (car pending-event) (file-notify--descriptor (caar pending-event))) |
| 193 | 'created | 194 | (funcall (cadr pending-event) (car pending-event)) |
| 194 | (setq file1 file | 195 | (setq pending-event nil)) |
| 195 | file (file-notify--event-file-name pending-event) | ||
| 196 | file-notify--pending-events | ||
| 197 | (delete pending-event file-notify--pending-events)) | ||
| 198 | 'renamed)))) | ||
| 199 | 196 | ||
| 200 | ;; Apply callback. | 197 | ;; Apply callback. |
| 201 | (when (and action | 198 | (when (and action |
| @@ -213,12 +210,10 @@ EVENT is the cdr of the event in `file-notify-handle-event' | |||
| 213 | (if file1 | 210 | (if file1 |
| 214 | (funcall | 211 | (funcall |
| 215 | callback | 212 | callback |
| 216 | `(,(file-notify--descriptor desc (nth 0 entry)) | 213 | `(,(file-notify--descriptor desc) ,action ,file ,file1)) |
| 217 | ,action ,file ,file1)) | ||
| 218 | (funcall | 214 | (funcall |
| 219 | callback | 215 | callback |
| 220 | `(,(file-notify--descriptor desc (nth 0 entry)) | 216 | `(,(file-notify--descriptor desc) ,action ,file)))))))) |
| 221 | ,action ,file)))))))) | ||
| 222 | 217 | ||
| 223 | ;; `gfilenotify' and `w32notify' return a unique descriptor for every | 218 | ;; `gfilenotify' and `w32notify' return a unique descriptor for every |
| 224 | ;; `file-notify-add-watch', while `inotify' returns a unique | 219 | ;; `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." | |||
| 325 | file-notify-descriptors) | 320 | file-notify-descriptors) |
| 326 | 321 | ||
| 327 | ;; Return descriptor. | 322 | ;; Return descriptor. |
| 328 | (file-notify--descriptor | 323 | (file-notify--descriptor desc))) |
| 329 | desc (unless (file-directory-p file) (file-name-nondirectory file))))) | ||
| 330 | 324 | ||
| 331 | (defun file-notify-rm-watch (descriptor) | 325 | (defun file-notify-rm-watch (descriptor) |
| 332 | "Remove an existing watch specified by its DESCRIPTOR. | 326 | "Remove an existing watch specified by its DESCRIPTOR. |