@@ -124,17 +124,18 @@ type Watcher struct {
124124 // Errors sends any errors.
125125 Errors chan error
126126
127- done chan struct {}
128- kq int // File descriptor (as returned by the kqueue() syscall).
129- closepipe [2 ]int // Pipe used for closing.
130- mu sync.Mutex // Protects access to watcher data
131- watches map [string ]int // Watched file descriptors (key: path).
132- watchesByDir map [string ]map [int ]struct {} // Watched file descriptors indexed by the parent directory (key: dirname(path)).
133- userWatches map [string ]struct {} // Watches added with Watcher.Add()
134- dirFlags map [string ]uint32 // Watched directories to fflags used in kqueue.
135- paths map [int ]pathInfo // File descriptors to path names for processing kqueue events.
136- fileExists map [string ]struct {} // Keep track of if we know this file exists (to stop duplicate create events).
137- isClosed bool // Set to true when Close() is first called
127+ done chan struct {}
128+ kq int // File descriptor (as returned by the kqueue() syscall).
129+ closepipe [2 ]int // Pipe used for closing.
130+ mu sync.Mutex // Protects access to watcher data
131+ watches map [string ]int // Watched file descriptors (key: path).
132+ watchesByDir map [string ]map [int ]struct {} // Watched file descriptors indexed by the parent directory (key: dirname(path)).
133+ userWatches map [string ]struct {} // Watches added with Watcher.Add()
134+ dirFlags map [string ]uint32 // Watched directories to fflags used in kqueue.
135+ nonRecursiveDirs map [string ]struct {} // Watched directories that should not watch children.
136+ paths map [int ]pathInfo // File descriptors to path names for processing kqueue events.
137+ fileExists map [string ]struct {} // Keep track of if we know this file exists (to stop duplicate create events).
138+ isClosed bool // Set to true when Close() is first called
138139}
139140
140141type pathInfo struct {
@@ -162,17 +163,18 @@ func NewBufferedWatcher(sz uint) (*Watcher, error) {
162163 }
163164
164165 w := & Watcher {
165- kq : kq ,
166- closepipe : closepipe ,
167- watches : make (map [string ]int ),
168- watchesByDir : make (map [string ]map [int ]struct {}),
169- dirFlags : make (map [string ]uint32 ),
170- paths : make (map [int ]pathInfo ),
171- fileExists : make (map [string ]struct {}),
172- userWatches : make (map [string ]struct {}),
173- Events : make (chan Event , sz ),
174- Errors : make (chan error ),
175- done : make (chan struct {}),
166+ kq : kq ,
167+ closepipe : closepipe ,
168+ watches : make (map [string ]int ),
169+ watchesByDir : make (map [string ]map [int ]struct {}),
170+ dirFlags : make (map [string ]uint32 ),
171+ nonRecursiveDirs : make (map [string ]struct {}),
172+ paths : make (map [int ]pathInfo ),
173+ fileExists : make (map [string ]struct {}),
174+ userWatches : make (map [string ]struct {}),
175+ Events : make (chan Event , sz ),
176+ Errors : make (chan error ),
177+ done : make (chan struct {}),
176178 }
177179
178180 go w .readEvents ()
@@ -313,15 +315,23 @@ func (w *Watcher) AddWith(name string, opts ...addOpt) error {
313315 w .mu .Lock ()
314316 w .userWatches [name ] = struct {}{}
315317 w .mu .Unlock ()
316- _ , err := w .addWatch (name , noteAllEvents )
318+ _ , err := w .addWatch (name , noteAllEvents , true )
317319 return err
318320}
319321
320322func (w * Watcher ) AddWithEvents (name string , flags uint32 ) error {
321323 w .mu .Lock ()
322324 w .userWatches [name ] = struct {}{}
323325 w .mu .Unlock ()
324- _ , err := w .addWatch (name , flags )
326+ _ , err := w .addWatch (name , flags , true )
327+ return err
328+ }
329+
330+ func (w * Watcher ) AddNonRecursive (name string , flags uint32 ) error {
331+ w .mu .Lock ()
332+ w .userWatches [name ] = struct {}{}
333+ w .mu .Unlock ()
334+ _ , err := w .addWatch (name , flags , false )
325335 return err
326336}
327337
@@ -371,6 +381,7 @@ func (w *Watcher) remove(name string, unwatchFiles bool) error {
371381
372382 delete (w .paths , watchfd )
373383 delete (w .dirFlags , name )
384+ delete (w .nonRecursiveDirs , name )
374385 delete (w .fileExists , name )
375386 w .mu .Unlock ()
376387
@@ -421,7 +432,7 @@ const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | un
421432// described in kevent(2).
422433//
423434// Returns the real path to the file which was added, with symlinks resolved.
424- func (w * Watcher ) addWatch (name string , flags uint32 ) (string , error ) {
435+ func (w * Watcher ) addWatch (name string , flags uint32 , recursive bool ) (string , error ) {
425436 var isDir bool
426437 name = filepath .Clean (name )
427438
@@ -522,9 +533,15 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
522533 w .mu .Lock ()
523534
524535 watchDir := (flags & unix .NOTE_WRITE ) == unix .NOTE_WRITE &&
536+ recursive &&
525537 (! alreadyWatching || (w .dirFlags [name ]& unix .NOTE_WRITE ) != unix .NOTE_WRITE )
526538 // Store flags so this watch can be updated later
527539 w .dirFlags [name ] = flags
540+ if recursive {
541+ delete (w .nonRecursiveDirs , name )
542+ } else {
543+ w .nonRecursiveDirs [name ] = struct {}{}
544+ }
528545 w .mu .Unlock ()
529546
530547 if watchDir {
@@ -584,7 +601,11 @@ func (w *Watcher) readEvents() {
584601 w .mu .Unlock ()
585602 }
586603
587- if path .isDir && event .Has (Write ) && ! event .Has (Remove ) {
604+ w .mu .Lock ()
605+ _ , nonRecursive := w .nonRecursiveDirs [event .Name ]
606+ w .mu .Unlock ()
607+
608+ if path .isDir && event .Has (Write ) && ! event .Has (Remove ) && ! nonRecursive {
588609 w .sendDirectoryChangeEvents (event .Name )
589610 } else {
590611 if ! w .sendEvent (event ) {
@@ -752,11 +773,11 @@ func (w *Watcher) internalWatch(name string, fi os.FileInfo) (string, error) {
752773 w .mu .Unlock ()
753774
754775 flags |= unix .NOTE_DELETE | unix .NOTE_RENAME
755- return w .addWatch (name , flags )
776+ return w .addWatch (name , flags , true )
756777 }
757778
758779 // watch file to mimic Linux inotify
759- return w .addWatch (name , noteAllEvents )
780+ return w .addWatch (name , noteAllEvents , true )
760781}
761782
762783// Register events with the queue.
0 commit comments