One of the features I use Ranger for is to visually select multiple music files and then open them with mpv to play. Would that be something Hunter supports, or would support in the future?
What has been the most challenging aspect you've encountered so far while building this?
Thanks for sharing!
Yes, you can select multiple files with space and open them all at once using the exec feature (type "!"). When you press tab it will insert "$s", which will then be substituted with either the normal selection, or when multiple files are selected with those multiple files. This works pretty much like in ranger. It doesn't work when you just open them by "entering" them. I'm not sure if that even makes sense since different file types could be selected and it's not clear what the right thing to do would be. Maybe opening them one after another? I'm not actually sure what ranger does in that case.
The most difficult thing.. That's actually hard to say since I started this to learn more about Rust and it's the largest project I've written so far at >8000 loc, so basically everything. :) But specifically, I guess getting the asynchronous stuff working right was the hardest part. hunter loads everything asynchronously and on demand, like the metadata and directory size. This is to speed up loading times. I'm still not sure it's 100% correct. Rust makes it easy to prevent data races, and it's very strict about what you can share across threads and how you do it, but it doesn't prevent race conditions and logic errors, and reasoning about concurrent programs is still hard.
I actually implemented my own Async type instead of using Futures or something pre-made, because as far as I can tell especially futures come with a pretty heavy binary size cost, which in turn takes time to load, making startup slower on a slow HDD. Other than that just getting the design right was pretty hard. Although the basic foundation is pretty solid I think.
Another reason I started this is to see how usable my TUI widgets are and when I first scrolled through a directory listing at light speed I got somewhat addicted. :) I kept those widgets really generic and they're easy to use in the sense that you can for example get a ListView for your type by just implementing a trait for it and they're very composable so that you can easily get a TabView for your ListView and so on. My plan is to put all that in a library eventually.
Error handling is another problem area. When I started I actually didn't make use of any error handling at all, in the sense that I didn't return errors from my functions, instead handling everything by just returning early, or returning empty strings or whatever made sense. This got problematic after I started heavily using mutexes, channels and other things that return errors, since I couldn't use ? and most of the time there is really nothing to do other than returning early with the error (and logging it), or skipping that operation when it happens in a loop.
I then went the other extreme and started returning Results everywhere. The problem is that right now there is just one single enum with all the dozens of possible errors, so it's hard to say what error can actually happen. Another problem is that I had to remove backtraces after implementing the Async type since backtraces can't be shared between threads for some reason and now the log viewer is pretty crippled. It's actually a "FoldView" and you could fold those backtraces in and out to see what went wrong where, now it's pretty much just a simple list. So yeah, splitting those error types is definitely on my todo list. :)
EDIT: fixed typo