aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Albinus2015-09-06 14:21:56 +0200
committerMichael Albinus2015-09-06 14:21:56 +0200
commitdbdc459a48091f5953faf14bcaaa7e6d37fbf024 (patch)
tree5ea667562039d35f4e6def4aec933ca0149c3988
parent29b0e0bb2c4e09465b97497ba96886e90a64ad71 (diff)
downloademacs-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.texi22
-rw-r--r--lisp/filenotify.el114
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
2677Whether the action @code{renamed} is returned, depends on the used 2677Whether the action @code{renamed} is returned, depends on the used
2678watch library. It can be expected, when a directory is watched, and 2678watch library. Otherwise, the actions @code{deleted} and
2679both @var{file} and @var{file1} belong to this directory. Otherwise, 2679@code{created} could be returned in a random order.
2680the actions @code{deleted} and @code{created} could be returned in a
2681random 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.
55If EVENT is a filewatch event, call its callback. It has the format 55If 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
59Otherwise, signal a `file-notify-error'." 59Otherwise, 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
69The entries are a list (DESCRIPTOR ACTION FILE COOKIE). ACTION 69 "A pending file notification events for a future `renamed' action.
70is either `moved-from' or `renamed-from'.") 70It 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'.
97For `gfilenotify' and `w32notify' it is the same descriptor as 97For `gfilenotify' and `w32notify' it is the same descriptor as
98used in the low-level file notification package." 98used 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.
107EVENT is the cdr of the event in `file-notify-handle-event' 108EVENT 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.