Skip to content

Commit

Permalink
feat: add watch command
Browse files Browse the repository at this point in the history
  • Loading branch information
rdavisau committed Aug 28, 2022
1 parent fd45166 commit 8d876cb
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 11 deletions.
2 changes: 2 additions & 0 deletions readme.md
Expand Up @@ -103,6 +103,8 @@ The host supports a small set of commands that were useful to me:

`reset`: resets the state of the incremental compiler - removes all trees and references, and asks the target to resend dependencies.

`watch {path}`: relative to the last changed file's directory, recursively finds all .cs in at `path` and below and adds them to the incremental compilation. e.g. `watch .` to include all files in the same directory as the current file being changed, without having to trigger changes against them all individually

### target commands

Commands prefixed with '!' will be sent to targets rather than the host. There are no built-in commands supported by the target, but you can add support for arbitrary commands to your `IReloadManager` by implementing the `ExecuteCommand` method. For example, a `goto` command in a prism app could be implemented using the code below, which walks the service container for pages and allows them to be chosen from a menu (if not specified as an argument to the command):
Expand Down
76 changes: 65 additions & 11 deletions src/components/tbc.host/Components/FileWatcher/FileWatcher.cs
@@ -1,23 +1,29 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using RxFileSystemWatcher;
using Tbc.Host.Components.Abstractions;
using Tbc.Host.Components.CommandProcessor.Models;
using Tbc.Host.Components.FileWatcher.Models;
using Tbc.Host.Config;

namespace Tbc.Host.Components.FileWatcher
{
public class FileWatcher : ComponentBase<FileWatcher>, IFileWatcher
public class FileWatcher : ComponentBase<FileWatcher>, IFileWatcher, IExposeCommands
{
private readonly FileWatchConfig _config;
private readonly IFileSystem _fileSystem;

public string WatchPath { get; private set; }

public ChangedFile LastChangedFile { get; private set; }

private Subject<ChangedFile> _manualWatchFiles = new Subject<ChangedFile>();
public IObservable<ChangedFile> Changes { get; set; }

public FileWatcher(FileWatchConfig config, IFileSystem fileSystem, ILogger<FileWatcher> logger) : base(logger)
Expand All @@ -30,7 +36,10 @@ public FileWatcher(FileWatchConfig config, IFileSystem fileSystem, ILogger<FileW

private void Init()
{
Changes = CreateFileSystemWatcher(_config.RootPath, _config.FileMask);
Changes =
Observable.Merge(
_manualWatchFiles, // files added to the incremental by a 'watch' command
CreateFileSystemWatcher(_config.RootPath, _config.FileMask)); // files actually changed
}

public IObservable<ChangedFile> CreateFileSystemWatcher(string path, string mask)
Expand All @@ -53,37 +62,82 @@ public IObservable<ChangedFile> CreateFileSystemWatcher(string path, string mask

Logger.LogInformation("File watcher {FileWatcher} started with config {@Config}", ofsw, _config);

var ret =
var ret =
Observable
.Merge(ofsw.Changed, ofsw.Created, ofsw.Renamed, ofsw.Deleted)
.Where(x => !_config.Ignore.Any(i => x.FullPath.Contains((string) i)))
.Select(x => x.FullPath)
.Select(TryGetChangedFile)
.Where(x => x != null)
.Do(f => Logger.LogInformation("Changed File: {ChangedFile}", f.Path.Substring(WatchPath.Length)));

ofsw.Start();

return ret;
}

private ChangedFile TryGetChangedFile(FileSystemEventArgs x)
private ChangedFile TryGetChangedFile(string filePath)
{
try
{
return new ChangedFile
return LastChangedFile = new ChangedFile
{
Path = x.FullPath,
Contents = _fileSystem.File.ReadAllText(x.FullPath)
Path = filePath,
Contents = _fileSystem.File.ReadAllText(filePath)
};
}
catch (Exception ex)
{
Logger.LogError(ex,
"An error occurred when attempting to read changed file at '{FilePath}'",
x.FullPath);
filePath);

return null;
}
}

public string Identifier => $"fw";
public IEnumerable<TbcCommand> Commands => new[]
{
new TbcCommand
{
Command = "watch",
Execute = HandleWatchCommand
}
};

private Task HandleWatchCommand(string cmd, string[] args)
{
if (args.Length != 1)
{
Logger.LogWarning("Need exactly one argument ('relative path') for command 'watch'");
return Task.CompletedTask;
}

if (LastChangedFile is null)
{
Logger.LogWarning("No previously changed file relative to which to watch");
return Task.CompletedTask;
}

var inputPath = args[0];

var lastPath = LastChangedFile.Path;
var lastDirectory = Path.GetDirectoryName(lastPath);

var targetPath = Path.Combine(lastDirectory, inputPath);
var filesInPath =
_fileSystem.Directory.GetFiles(targetPath, "*.cs", SearchOption.AllDirectories)
.Select(x => new FileInfo(x).FullName)
.ToList();

Logger.LogInformation("Watch with target {Target} resolve to {ResolvedPath} and includes files {@Files}",
inputPath, targetPath, filesInPath);

foreach (var file in filesInPath)
_manualWatchFiles.OnNext(TryGetChangedFile(file));

return Task.CompletedTask;
}
}
}

0 comments on commit 8d876cb

Please sign in to comment.