elves / elvish

Powerful scripting language & versatile interactive shell
https://elv.sh/
BSD 2-Clause "Simplified" License
5.53k stars 297 forks source link

Feature Request: Inotify module #1690

Closed fennewald closed 1 year ago

fennewald commented 1 year ago

This is a feature request for an inotify module to be added to the elvish stdlib. If accepted, I would develop this feature. I am primarily looking for @xiaq 's blessing to bloat add to the stdlib.

This module would provide two commands, watch and wait. They would function identically, with the only different being wait would exit after a single event was caught, and watch would continue indefinitely.

Function signatures

fn watch {|
    &events=[create delete modify]
    &debounce=250
    &timeout=0
    &recursive=$true
    &rich-output=$false
    @files
| ... }

events would be a list of events to be caught. By default, it would consist of create, delete, and modify events, but the full list of valid events would be(stolen directly from inotifywatch):

access      file or directory contents were read
modify      file or directory contents were written
attrib      file or directory attributes changed
close_write file or directory closed, after being opened in
            writable mode
close_nowrite   file or directory closed, after being opened in
            read-only mode
close       file or directory closed, regardless of read/write mode
open        file or directory opened
moved_to    file or directory moved to watched directory
moved_from  file or directory moved from watched directory
move        file or directory moved to or from watched directory
move_self       A watched file or directory was moved.
create      file or directory created within watched directory
delete      file or directory deleted within watched directory
delete_self file or directory was deleted
unmount     file system containing file or directory unmounted

debounce would be a duration, in milliseconds, to debounce events by. If two events are registered within debounce time of each other, they are combined and sent as one event.

timeout is a timeout of the maximum duration without edits. If timeout passes without any events being received, the command will exit (without error). If set to 0, no timeout is used.

recursive is a flag to determine if directories should be recursively watched.

rich-output is a flag to determine if the rich output format should be used. When false, each event sends only the modified filename on the value channel. For example:

~> watch &rich-output=$false .
▶ filename1.txt
▶ filename2.txt
▶ filename3.txt

If true, each event sends a key-value object, with the filename, list of captured events, and timestamp of the events. For combined, debounced events, the latest timestamp is sent:

~> watch &rich-output=$true .
▶ [&path=filename1.txt &time=(num 1681497234) &events=[access modify]]

@files is a list of files to apply watches to.

Example usage

Running a lambda each time a file was modified

~> watch . | each {|f|
    echo $f modified
}
filename1.txt modified
filename2.txt modified

Decision points

There are a few decisions made that are arbitrary, and may not be the best choice. Open to suggestions

Caveats

Alternatives

One solution would be to wrap the already existing inotify-tools, available on most linux package managers. I don't think this is the best approach. These tools lack critical features, like debouncing and output parsing. These additional features are complex enough that implementing them in an elvish wrapper would be non-trivial.

However, developing the behavior above as a separate command, that implements all the logic defined above, and simply outputs json, could be wrapped relatively easily by elvish, and might make more sense than an addition to the stdlib.

fennewald commented 1 year ago

looking into the golang file watching ecosystem, it seems the list of events above is likely more verbose than needed.

I plan on using fsnotify, which means this will be cross-platform, but will only support Create, Write, Rename, Remove, Chmod events

xiaq commented 1 year ago

Regarding the design itself:

But...

However, developing the behavior above as a separate command, that implements all the logic defined above, and simply outputs json, could be wrapped relatively easily by elvish, and might make more sense than an addition to the stdlib.

I agree with this :) Developing a command that is based on fsnotify and outputs JSON makes a lot of sense.

I do like the idea of having this well integrated into Elvish - the output is structured, so it's quite clumsy in traditional shells but very easy in Elvish. If you'd like to build an Elvish module that wraps it, that's something I'm thinking about in #1607 (case 3).

fennewald commented 1 year ago

Makes sense :). I have a few thoughts about #1607 I'll leave there.

krader1961 commented 1 year ago

Note that there is no reason this needs to be limited to working on Linux. The fswatch command shows that it is possible to support Linux, the various BSDs (including macOS), and Windows. In fact, if you're going to write a module that wraps an external command to make using the external command easier to use with Elvish I would argue that fswatch is a better choice given that it also works on all the platforms supported by Elvish.