[replace-across-files] Replace in Files

This is all the work for Replace in Files after the find bar unification (which is in #7705). There were probably a few changes to the find bar after that PR, but the work was fairly separable (and some of the code moved around after that), so it's probably worth reviewing that one first/separately anyway.

High-level points:

* `FindInFiles` was refactored into multiple pieces:
    * `FindInFiles` is now just the cross-file search and replace engine code, with no UI logic. (The replace code is actually primarily in FindUtils, because I thought I was going to need it in two places, but it turns out I don't because single-file Replace All went away - see below.) FindInFiles also takes care of listening for document/file change events and updating the SearchModel incrementally. (Note, however, that those updates only happen in Find mode. In Replace mode, we shut the panel immediately whenever a change happens to any document that was found by the initial search, to avoid complexity with updating the checkbox state, etc. We have a [Trello card](https://trello.com/c/tfTFQV8e) for more sophisticated update behavior during Replace.)
    * `FindBar` handles the modal bar UI for both single-file Find/Replace and Find/Replace In Files (that work was done in #7705).
    * `SearchModel` encapsulates the query and results state for Find In Files and Replace In Files.
    * `SearchResultsView` manages the results panel for Find In Files and Replace In Files, including paging and (in replace mode) the checkboxes for choosing what to replace. It listens to SearchModel for updates to the results. This is mostly from work done by @TomMalbran to unify the old single-file Replace All panel with the Find in Files panel.
    * `FindInFilesUI` is the controller module that sews all the above together and actually handles the menu commands, etc.
* Single-file `Replace All` is no longer has its code for running the search/replace; it just delegates to FindInFilesUI, passing the current file as the scope. Also, we took the opportunity to rename it from `All...` to `Batch...` since we've heard people be confused by the original naming.
* You can now safely kick off a search or replace programmatically by calling `FindInFilesUI.searchAndShowResults()` with the query/scope/filter info (as requested in #7445). If you wanted, you could even do a completely headless search or replace by calling directly into `FindInFiles.doSearchInScope()`.

I'll add comments throughout the diffs on what has changed.
Might want to clarify why it's possible for this to be null.  (I think it only happens in cases where that file failed replacement for some reason -- either couldn't be read, or left unchanged (& thus Document is GC'ed) due to timestamp mismatch).