From 9a1749c1b23637b4fe40c378d45630dd81a81e79 Mon Sep 17 00:00:00 2001
From: Gs-itisitcat <66198929+Gs-itisitcat@users.noreply.github.com>
Date: Wed, 22 May 2024 19:39:00 +0900
Subject: [PATCH 1/3] Create DirectorySearcherBase and move some properties
from ISearcher
---
.../Searcher/DirectorySearcherBase.cs | 56 ++++++++++++++++++
.../Searcher/ISearcher.cs | 10 ----
.../NonRecursiveRepositorySearcher.cs | 58 ++++--------------
.../Searcher/RecursiveRepositorySearcher.cs | 59 ++++---------------
4 files changed, 79 insertions(+), 104 deletions(-)
create mode 100644 local-repository-listing/Searcher/DirectorySearcherBase.cs
diff --git a/local-repository-listing/Searcher/DirectorySearcherBase.cs b/local-repository-listing/Searcher/DirectorySearcherBase.cs
new file mode 100644
index 0000000..d7152b4
--- /dev/null
+++ b/local-repository-listing/Searcher/DirectorySearcherBase.cs
@@ -0,0 +1,56 @@
+
+using Microsoft.Extensions.FileSystemGlobbing;
+
+namespace LocalRepositoryListing.Searcher;
+
+public abstract class DirectorySearcherBase : ISearcher
+{
+ protected static readonly string _rootSearchPattern = "*";
+ protected static readonly string _searchPattern = ".git";
+ ///
+ /// Gets the root directories to search in.
+ ///
+ public IReadOnlyCollection RootDirectories { get; init; }
+
+ ///
+ /// Gets the paths to exclude from the search.
+ ///
+ public IReadOnlyCollection ExcludePaths { get; init; }
+
+ ///
+ /// Gets the directory names to exclude from the search.
+ ///
+ public IReadOnlyCollection ExcludeNames { get; init; }
+
+ private readonly Matcher _nameMatcher = new();
+
+ public DirectorySearcherBase(IList rootDirectories, IList excludePaths, IList excludeNames)
+ {
+ RootDirectories = rootDirectories.AsReadOnly();
+ ExcludePaths = excludePaths.AsReadOnly();
+ ExcludeNames = excludeNames.AsReadOnly();
+ _nameMatcher.AddIncludePatterns(excludeNames);
+ }
+
+ protected abstract EnumerationOptions EnumerationOptions { get; }
+
+ ///
+ /// Determines if the specified directory matches the exclusion criteria.
+ ///
+ /// The object representing the directory to check.
+ /// true if the directory matches the exclusion criteria; otherwise, false.
+ protected bool IsMatchExclude(DirectoryInfo directoryInfo)
+ {
+ return directoryInfo.FullName
+ .Split(Path.DirectorySeparatorChar)
+ .Where(p => !string.IsNullOrEmpty(p))
+ .Any(p => _nameMatcher.Match(p).HasMatches)
+ || ExcludePaths
+ .Any(p => directoryInfo.FullName
+ .Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
+ .Contains(p)
+ );
+ }
+
+ public abstract ParallelQuery Search(CancellationToken cancellationToken);
+}
diff --git a/local-repository-listing/Searcher/ISearcher.cs b/local-repository-listing/Searcher/ISearcher.cs
index 0840f85..8befc92 100644
--- a/local-repository-listing/Searcher/ISearcher.cs
+++ b/local-repository-listing/Searcher/ISearcher.cs
@@ -4,16 +4,6 @@
///
public interface ISearcher
{
- ///
- /// Gets the root directories to search in.
- ///
- public IReadOnlyCollection RootDirectories { get; }
-
- ///
- /// Gets the paths to exclude from the search.
- ///
- public IReadOnlyCollection ExcludePaths { get; }
-
///
/// Gets the directory names to exclude from the search.
///
diff --git a/local-repository-listing/Searcher/NonRecursiveRepositorySearcher.cs b/local-repository-listing/Searcher/NonRecursiveRepositorySearcher.cs
index fd8a63d..c3e2ef3 100644
--- a/local-repository-listing/Searcher/NonRecursiveRepositorySearcher.cs
+++ b/local-repository-listing/Searcher/NonRecursiveRepositorySearcher.cs
@@ -1,17 +1,17 @@
-using Microsoft.Extensions.FileSystemGlobbing;
-
-namespace LocalRepositoryListing.Searcher;
+namespace LocalRepositoryListing.Searcher;
///
/// Represents a class that searches for local repositories within specified root directories.
///
-public class NonRecursiveRepositorySearcher : ISearcher
+///
+/// Initializes a new instance of the class.
+///
+/// The root directories to search in.
+/// The paths to exclude from the search.
+/// The names to exclude from the search.
+public class NonRecursiveRepositorySearcher(IList rootDirectories, IList excludePaths, IList excludeNames) : DirectorySearcherBase(rootDirectories, excludePaths, excludeNames), ISearcher
{
- public IReadOnlyCollection RootDirectories { get; init; }
- public IReadOnlyCollection ExcludePaths { get; init; }
- public IReadOnlyCollection ExcludeNames { get; init; }
-
- private static readonly EnumerationOptions _enumerationOptions = new()
+ protected override EnumerationOptions EnumerationOptions { get; } = new()
{
RecurseSubdirectories = false,
IgnoreInaccessible = true,
@@ -30,48 +30,14 @@ public class NonRecursiveRepositorySearcher : ISearcher
/// An array of root directories to search for repositories.
public NonRecursiveRepositorySearcher(IList rootDirectories) : this(rootDirectories, [], []) { }
- ///
- /// Initializes a new instance of the class.
- ///
- /// The root directories to search in.
- /// The paths to exclude from the search.
- /// The names to exclude from the search.
- public NonRecursiveRepositorySearcher(IList rootDirectories, IList excludePaths, IList excludeNames)
- {
- RootDirectories = rootDirectories.AsReadOnly();
- ExcludePaths = excludePaths.AsReadOnly();
- ExcludeNames = excludeNames.AsReadOnly();
- _nameMatcher.AddIncludePatterns(excludeNames);
- }
-
- private readonly Matcher _nameMatcher = new();
-
- ///
- /// Determines if the specified directory matches the exclusion criteria.
- ///
- /// The object representing the directory to check.
- /// true if the directory matches the exclusion criteria; otherwise, false.
- private bool IsMatchExclude(DirectoryInfo directoryInfo)
- {
- return directoryInfo.FullName
- .Split(Path.DirectorySeparatorChar)
- .Where(p => !string.IsNullOrEmpty(p))
- .Any(p => _nameMatcher.Match(p).HasMatches)
- || ExcludePaths
- .Any(p => directoryInfo.FullName
- .Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
- .Contains(p)
- );
- }
-
- public ParallelQuery Search(CancellationToken cancellationToken)
+ public override ParallelQuery Search(CancellationToken cancellationToken)
{
return RootDirectories
.AsParallel()
.WithCancellation(cancellationToken)
- .SelectMany(d => Directory.EnumerateDirectories(d, "*", _enumerationOptions))
+ .SelectMany(d => Directory.EnumerateDirectories(d, _rootSearchPattern, EnumerationOptions))
.Select(d => new DirectoryInfo(d))
- .Where(d => d.GetDirectories(".git", SearchOption.TopDirectoryOnly).Any())
+ .Where(d => d.GetDirectories(_searchPattern, SearchOption.TopDirectoryOnly).Length != 0)
.Where(d => !IsMatchExclude(d));
}
}
diff --git a/local-repository-listing/Searcher/RecursiveRepositorySearcher.cs b/local-repository-listing/Searcher/RecursiveRepositorySearcher.cs
index 08a2543..43a5da3 100644
--- a/local-repository-listing/Searcher/RecursiveRepositorySearcher.cs
+++ b/local-repository-listing/Searcher/RecursiveRepositorySearcher.cs
@@ -1,19 +1,16 @@
-using Microsoft.Extensions.FileSystemGlobbing;
-using System.Collections.ObjectModel;
-
-namespace LocalRepositoryListing.Searcher;
+namespace LocalRepositoryListing.Searcher;
///
/// Represents a class that searches for local repositories within specified root directories.
///
-public class RecursiveRepositorySearcher : ISearcher
+///
+/// Initializes a new instance of the class.
+///
+/// The root directories to search in.
+/// The paths to exclude from the search.
+/// The names to exclude from the search.
+public class RecursiveRepositorySearcher(IList rootDirectories, IList excludePaths, IList excludeNames) : DirectorySearcherBase(rootDirectories, excludePaths, excludeNames), ISearcher
{
- private static readonly string _rootSearchPattern = "*";
- private static readonly string _searchPattern = ".git";
- public IReadOnlyCollection RootDirectories { get; init; }
- public IReadOnlyCollection ExcludePaths { get; init; }
- public IReadOnlyCollection ExcludeNames { get; init; }
-
private static readonly EnumerationOptions _rootEnumerationOptions = new()
{
RecurseSubdirectories = false,
@@ -27,7 +24,7 @@ public class RecursiveRepositorySearcher : ISearcher
| FileAttributes.ReparsePoint,
};
- private static readonly EnumerationOptions _enumerationOptions = new()
+ protected override EnumerationOptions EnumerationOptions { get; } = new()
{
RecurseSubdirectories = true,
IgnoreInaccessible = true,
@@ -40,52 +37,18 @@ public class RecursiveRepositorySearcher : ISearcher
| FileAttributes.ReparsePoint,
};
- private readonly Matcher _nameMatcher = new();
-
- ///
- /// Determines if the specified directory matches the exclusion criteria.
- ///
- /// The object representing the directory to check.
- /// true if the directory matches the exclusion criteria; otherwise, false.
- private bool IsMatchExclude(DirectoryInfo directoryInfo)
- {
- return directoryInfo.FullName
- .Split(Path.DirectorySeparatorChar)
- .Where(p => !string.IsNullOrEmpty(p))
- .Any(p => _nameMatcher.Match(p).HasMatches)
- || ExcludePaths
- .Any(p => directoryInfo.FullName
- .Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
- .Contains(p)
- );
- }
-
///
/// Initializes a new instance of the RepositorySearcher class with the specified root directories.
///
/// An array of root directories to search for repositories.
public RecursiveRepositorySearcher(IList rootDirectories) : this(rootDirectories, [], []) { }
- ///
- /// Initializes a new instance of the class.
- ///
- /// The root directories to search in.
- /// The paths to exclude from the search.
- /// The names to exclude from the search.
- public RecursiveRepositorySearcher(IList rootDirectories, IList excludePaths, IList excludeNames)
- {
- RootDirectories = rootDirectories.AsReadOnly();
- ExcludePaths = excludePaths.AsReadOnly();
- ExcludeNames = excludeNames.AsReadOnly();
- _nameMatcher.AddIncludePatterns(excludeNames);
- }
-
///
/// Initializes a new instance of the RepositorySearcher class with the logical drives as the root directories.
///
public RecursiveRepositorySearcher() : this(Environment.GetLogicalDrives()) { }
- public ParallelQuery Search(CancellationToken cancellationToken)
+ public override ParallelQuery Search(CancellationToken cancellationToken)
{
return RootDirectories
.AsParallel()
@@ -94,7 +57,7 @@ public ParallelQuery Search(CancellationToken cancellationToken)
.AsParallel() // Somehow faster with this additional AsParallel()
.WithCancellation(cancellationToken)
.Where(d => !IsMatchExclude(new DirectoryInfo(d)))
- .SelectMany(d => Directory.EnumerateDirectories(d, _searchPattern, _enumerationOptions))
+ .SelectMany(d => Directory.EnumerateDirectories(d, _searchPattern, EnumerationOptions))
.Select(d => Directory.GetParent(d))
.Where(d => d != null)
.Select(d => d ?? throw new InvalidOperationException("Directory.GetParent returns null"))
From 878ff68fa3ae5b119fa8b7361950f36f0d67ac69 Mon Sep 17 00:00:00 2001
From: Gs-itisitcat <66198929+Gs-itisitcat@users.noreply.github.com>
Date: Thu, 23 May 2024 00:29:17 +0900
Subject: [PATCH 2/3] Use R3 for handling search result
---
.../ListLocalReposCommand.cs | 14 ++--
.../ResultLister/ConsoleOutputLister.cs | 58 +++++++++++++
.../FZFLister.cs} | 13 ++-
.../ResultLister/FuzzyFinderListerBase.cs | 72 ++++++++++++++++
.../ResultLister/IResultLister.cs | 11 +++
.../ResultProcessor/ConsoleOutputProcessor.cs | 60 --------------
.../FuzzyFinderProcessorBase.cs | 82 -------------------
.../ResultProcessor/ISearchResultProcessor.cs | 12 ---
.../Searcher/DirectorySearcherBase.cs | 18 +++-
.../Searcher/ISearcher.cs | 13 ++-
.../NonRecursiveRepositorySearcher.cs | 25 ++++--
.../Searcher/RecursiveRepositorySearcher.cs | 30 ++++---
.../local-repository-listing.csproj | 3 +-
13 files changed, 218 insertions(+), 193 deletions(-)
create mode 100644 local-repository-listing/ResultLister/ConsoleOutputLister.cs
rename local-repository-listing/{ResultProcessor/FZFProcessor.cs => ResultLister/FZFLister.cs} (67%)
create mode 100644 local-repository-listing/ResultLister/FuzzyFinderListerBase.cs
create mode 100644 local-repository-listing/ResultLister/IResultLister.cs
delete mode 100644 local-repository-listing/ResultProcessor/ConsoleOutputProcessor.cs
delete mode 100644 local-repository-listing/ResultProcessor/FuzzyFinderProcessorBase.cs
delete mode 100644 local-repository-listing/ResultProcessor/ISearchResultProcessor.cs
diff --git a/local-repository-listing/ListLocalReposCommand.cs b/local-repository-listing/ListLocalReposCommand.cs
index 0ca16e0..31fab28 100644
--- a/local-repository-listing/ListLocalReposCommand.cs
+++ b/local-repository-listing/ListLocalReposCommand.cs
@@ -35,7 +35,7 @@ You can use glob patterns.
/// Arguments to pass to the fuzzy finder process.
/// The result of the command execution.
[RootCommand]
- public int Lepol(
+ public async ValueTask Lepol(
[Option(0, ArgumentDescription)] string arg = "",
[Option("r", RootDescription)] string root = "",
[Option("l", ListOnlyDescription)] bool listOnly = false,
@@ -52,15 +52,11 @@ public int Lepol(
? new NonRecursiveRepositorySearcher(rootDirectories, excludePaths ?? [], excludeNames ?? [])
: new RecursiveRepositorySearcher(rootDirectories, excludePaths ?? [], excludeNames ?? []);
- ISearchResultProcessor processor = listOnly
- ? new ConsoleOutputProcessor(arg)
- : new FZFProcessor(arg, fuzzyFinderArgs ?? []);
+ IResultLister listable = listOnly
+ ? new ConsoleOutputLister(searcher, arg)
+ : new FZFLister(searcher, arg, fuzzyFinderArgs ?? []);
- // Pass the cancellation token source of search cancellation token to the fuzzy finder process
- // to cancel the search when the fuzzy finder process is terminated.
- var cts = new CancellationTokenSource();
- Context.CancellationToken.Register(cts.Cancel);
- return processor.ProcessSearchResult(searcher.Search(cts.Token), cts);
+ return await listable.ExecuteListing(Context.CancellationToken);
}
}
diff --git a/local-repository-listing/ResultLister/ConsoleOutputLister.cs b/local-repository-listing/ResultLister/ConsoleOutputLister.cs
new file mode 100644
index 0000000..69a6985
--- /dev/null
+++ b/local-repository-listing/ResultLister/ConsoleOutputLister.cs
@@ -0,0 +1,58 @@
+using LocalRepositoryListing.Searcher;
+using R3;
+namespace LocalRepositoryListing.ResultProcessor;
+
+///
+/// Initializes a new instance of the class with the specified search pattern.
+///
+/// The object representing the searcher.
+/// The search pattern to match against the full names of the directories.
+public class ConsoleOutputLister(ISearcher searcher, string searchPattern) : IResultLister
+{
+ ///
+ /// The search pattern to match against the full names of the directories.
+ ///
+ private readonly string _searchPattern = searchPattern;
+ private readonly ISearcher _searcher = searcher;
+
+ public async ValueTask ExecuteListing(CancellationToken cancellationToken)
+ {
+ using var searchSubscription = _searcher.SearchResults.Subscribe(d =>
+ {
+ var fullName = d.FullName.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
+
+ if (string.IsNullOrEmpty(fullName) || (!string.IsNullOrEmpty(_searchPattern) && !fullName.Contains(_searchPattern)))
+ {
+ return;
+ }
+
+ Console.WriteLine(fullName);
+ });
+
+ cancellationToken.Register(searchSubscription.Dispose);
+
+ try
+ {
+ var searchTask = _searcher.Search(cancellationToken);
+
+ while (
+ !cancellationToken.IsCancellationRequested
+ && !searchTask.IsCompleted
+ && !searchTask.IsFaulted
+ && !searchTask.IsCanceled
+ )
+ {
+ await Task.Delay(100, cancellationToken);
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ searchSubscription.Dispose();
+ Console.Error.WriteLine("Search was cancelled.");
+ return 1;
+ }
+ searchSubscription.Dispose();
+
+ return 0;
+ }
+}
diff --git a/local-repository-listing/ResultProcessor/FZFProcessor.cs b/local-repository-listing/ResultLister/FZFLister.cs
similarity index 67%
rename from local-repository-listing/ResultProcessor/FZFProcessor.cs
rename to local-repository-listing/ResultLister/FZFLister.cs
index 3a1887e..357c5a4 100644
--- a/local-repository-listing/ResultProcessor/FZFProcessor.cs
+++ b/local-repository-listing/ResultLister/FZFLister.cs
@@ -1,6 +1,13 @@
-namespace LocalRepositoryListing.ResultProcessor;
+using LocalRepositoryListing.Searcher;
-public class FZFProcessor(string? searchPattern, string[] args) : FuzzyFinderProcessorBase(arguments: [
+namespace LocalRepositoryListing.ResultProcessor;
+
+///
+/// Represents the processor for the FZF fuzzy finder.
+///
+///
+///
+public class FZFLister(ISearcher searcher, string? searchPattern, string[] args) : FuzzyFinderListerBase(searcher, arguments: [
"--ansi",
"--header",
"\"Select a git repository\"",
@@ -18,7 +25,7 @@ public class FZFProcessor(string? searchPattern, string[] args) : FuzzyFinderPro
"--query",
$"{(string.IsNullOrWhiteSpace(searchPattern) ? "\"\"" : searchPattern)}",
..args
- ])
+ ]), IResultLister
{
private static readonly string _fuzzyFinderName = "fzf";
public override string FuzzyFinderName => _fuzzyFinderName;
diff --git a/local-repository-listing/ResultLister/FuzzyFinderListerBase.cs b/local-repository-listing/ResultLister/FuzzyFinderListerBase.cs
new file mode 100644
index 0000000..dc1774a
--- /dev/null
+++ b/local-repository-listing/ResultLister/FuzzyFinderListerBase.cs
@@ -0,0 +1,72 @@
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using LocalRepositoryListing.Searcher;
+using R3;
+
+namespace LocalRepositoryListing.ResultProcessor;
+
+///
+/// Represents the base class for a fuzzy finder process.
+///
+public abstract class FuzzyFinderListerBase : IResultLister
+{
+ ///
+ /// Gets the name of the fuzzy finder.
+ ///
+ public abstract string FuzzyFinderName { get; }
+
+ ///
+ /// Gets the arguments for the processor.
+ ///
+ public ReadOnlyCollection Arguments => _arguments.AsReadOnly();
+ private readonly string[] _arguments = [];
+
+ private readonly ProcessStartInfo _processStartInfo;
+ private readonly ISearcher _searcher;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The object representing the searcher.
+ /// The arguments for the processor.
+ public FuzzyFinderListerBase(ISearcher searcher, string[] arguments)
+ {
+ _arguments = arguments;
+ _processStartInfo = new ProcessStartInfo(FuzzyFinderName)
+ {
+ // UseShellExecute is set to false to start the child process without using a shell
+ UseShellExecute = false,
+ RedirectStandardInput = true,
+ // For Non-ASCII characters
+ StandardInputEncoding = System.Text.Encoding.UTF8,
+ Arguments = string.Join(" ", arguments),
+ };
+
+ _searcher = searcher;
+ }
+
+ public ValueTask ExecuteListing(CancellationToken cancellationToken)
+ {
+ using var process = Process.Start(_processStartInfo);
+ if (process == null)
+ {
+ Console.Error.WriteLine($"Failed to start {FuzzyFinderName}");
+ return ValueTask.FromResult(1);
+ }
+
+ using var input = TextWriter.Synchronized(process.StandardInput);
+ if (input == null)
+ {
+ Console.Error.WriteLine($"Failed to get StandardInput of {FuzzyFinderName}");
+ return ValueTask.FromResult(1);
+ }
+
+ using var searchSubscription = _searcher.SearchResults.Subscribe(d => input.WriteLine(d.FullName.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)));
+
+ _ = _searcher.Search(cancellationToken);
+
+ process.WaitForExit();
+
+ return ValueTask.FromResult(process.ExitCode);
+ }
+}
diff --git a/local-repository-listing/ResultLister/IResultLister.cs b/local-repository-listing/ResultLister/IResultLister.cs
new file mode 100644
index 0000000..68e1fe4
--- /dev/null
+++ b/local-repository-listing/ResultLister/IResultLister.cs
@@ -0,0 +1,11 @@
+namespace LocalRepositoryListing.ResultProcessor;
+
+public interface IResultLister
+{
+ ///
+ /// Executes the listing operation.
+ ///
+ /// The cancellation token.
+ /// A representing the asynchronous operation, yielding the result of the listing operation.
+ public ValueTask ExecuteListing(CancellationToken cancellationToken);
+}
diff --git a/local-repository-listing/ResultProcessor/ConsoleOutputProcessor.cs b/local-repository-listing/ResultProcessor/ConsoleOutputProcessor.cs
deleted file mode 100644
index c2ad94e..0000000
--- a/local-repository-listing/ResultProcessor/ConsoleOutputProcessor.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-namespace LocalRepositoryListing.ResultProcessor;
-
-///
-/// Initializes a new instance of the class with the specified search pattern.
-///
-/// The search pattern to match against the full names of the directories.
-public class ConsoleOutputProcessor(string searchPattern) : ISearchResultProcessor
-{
- ///
- /// The search pattern to match against the full names of the directories.
- ///
- private readonly string _searchPattern = searchPattern;
-
-
- ///
- /// Processes the search result by printing the full names of the directories to the console.
- ///
- /// The search result containing the directories to be processed.
- /// The cancellation token source used to cancel the search.
- /// 0 if the search result was processed successfully, 1 if the search was cancelled.
- public int ProcessSearchResult(ParallelQuery searchResult, CancellationTokenSource searchCancellationTokenSource)
- {
- try
- {
- var searchTask = Task.Run(() =>
- searchResult
- .Select(d => d.FullName.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar))
- .Where(d => d.Contains(_searchPattern, StringComparison.OrdinalIgnoreCase))
- .ForAll(d =>
- Console.WriteLine(d)
- ), searchCancellationTokenSource.Token
- );
-
- // For immediate cancellation.
- // This is necessary because the searchTask is not cancelled immediately
- // when the searchCancellationTokenSource is cancelled by pressing Ctrl+C.
- while (
- !searchCancellationTokenSource.Token.IsCancellationRequested
- && !searchTask.IsCompleted
- && !searchTask.IsFaulted
- && !searchTask.IsCanceled
- )
- {
- Task.Delay(100).Wait();
- }
- }
- catch (OperationCanceledException)
- {
- Console.Error.WriteLine("Search was cancelled");
- return 1;
- }
-
- if (searchCancellationTokenSource.Token.IsCancellationRequested)
- {
- Console.Error.WriteLine("Search was cancelled");
- return 1;
- }
- return 0;
- }
-}
diff --git a/local-repository-listing/ResultProcessor/FuzzyFinderProcessorBase.cs b/local-repository-listing/ResultProcessor/FuzzyFinderProcessorBase.cs
deleted file mode 100644
index 4066139..0000000
--- a/local-repository-listing/ResultProcessor/FuzzyFinderProcessorBase.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using System.Collections.ObjectModel;
-using System.Diagnostics;
-
-namespace LocalRepositoryListing.ResultProcessor;
-
-///
-/// Represents the base class for a fuzzy finder process.
-///
-public abstract class FuzzyFinderProcessorBase : ISearchResultProcessor
-{
- ///
- /// Gets the name of the fuzzy finder.
- ///
- public abstract string FuzzyFinderName { get; }
-
- ///
- /// Gets the arguments for the processor.
- ///
- public ReadOnlyCollection Arguments => _arguments.AsReadOnly();
- private readonly string[] _arguments = [];
-
- private ProcessStartInfo _processStartInfo;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public FuzzyFinderProcessorBase(string[] arguments)
- {
- _arguments = arguments;
- _processStartInfo = new ProcessStartInfo(FuzzyFinderName)
- {
- // UseShellExecute is set to false to start the child process without using a shell
- UseShellExecute = false,
- RedirectStandardInput = true,
- // For Non-ASCII characters
- StandardInputEncoding = System.Text.Encoding.UTF8,
- Arguments = string.Join(" ", arguments),
- };
- }
-
- ///
- /// Processes the search result by starting the fuzzy finder process, writing the found repositories to its standard input,
- /// and waiting for the process to exit. If selection or cancellation occurs in fuzzy finder, the repository search is interrupted.
- ///
- /// The parallel query of DirectoryInfo objects representing the searched repositories.
- /// The CancellationTokenSource used to cancel the repository search.
- /// The exit code of the fuzzy finder process.
- public int ProcessSearchResult(ParallelQuery searched, CancellationTokenSource searchCancellationTokenSource)
- {
- using var process = Process.Start(_processStartInfo);
- if (process == null)
- {
- Console.Error.WriteLine($"Failed to start {FuzzyFinderName}");
- return 1;
- }
-
- // Get the standard input of the redirected fuzzy finder process
- // For thread safety, use a synchronized wrapper around the StandardInput stream
- using var input = TextWriter.Synchronized(process.StandardInput);
-
- if (input == null)
- {
- Console.Error.WriteLine($"Failed to get StandardInput of {FuzzyFinderName}");
- return 1;
- }
-
- // Write the found repositories to the standard input while fuzzy finder is running
- _ = Task.Run(() =>
- searched
- .Select(d => d.FullName.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar))
- .ForAll(d =>
- input.WriteLine(d)
- ), searchCancellationTokenSource.Token);
-
- // WaitForExitAsync() causes OperationCanceledException and prevents fuzzy finder from starting for some reason
- process.WaitForExit();
- // If selection or cancellation occurs in fuzzy finder, interrupt the repository search even if it is not finished
- searchCancellationTokenSource.Cancel();
-
- return process.ExitCode;
- }
-}
diff --git a/local-repository-listing/ResultProcessor/ISearchResultProcessor.cs b/local-repository-listing/ResultProcessor/ISearchResultProcessor.cs
deleted file mode 100644
index 1a203b8..0000000
--- a/local-repository-listing/ResultProcessor/ISearchResultProcessor.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace LocalRepositoryListing.ResultProcessor;
-
-public interface ISearchResultProcessor
-{
- ///
- /// Processes the search result.
- ///
- /// The search result.
- /// The cancellation token source for the search operation.
- /// Exit code of the search result processor.
- public int ProcessSearchResult(ParallelQuery searchResult, CancellationTokenSource searchCancellationTokenSource);
-}
diff --git a/local-repository-listing/Searcher/DirectorySearcherBase.cs b/local-repository-listing/Searcher/DirectorySearcherBase.cs
index d7152b4..895d958 100644
--- a/local-repository-listing/Searcher/DirectorySearcherBase.cs
+++ b/local-repository-listing/Searcher/DirectorySearcherBase.cs
@@ -1,12 +1,17 @@
using Microsoft.Extensions.FileSystemGlobbing;
+using R3;
namespace LocalRepositoryListing.Searcher;
public abstract class DirectorySearcherBase : ISearcher
{
+ protected readonly Subject _searchResults = new();
protected static readonly string _rootSearchPattern = "*";
protected static readonly string _searchPattern = ".git";
+
+ public Observable SearchResults { get; }
+
///
/// Gets the root directories to search in.
///
@@ -24,14 +29,25 @@ public abstract class DirectorySearcherBase : ISearcher
private readonly Matcher _nameMatcher = new();
+ ///
+ /// Initializes a new instance of the class with the specified root directories, paths to exclude, and names to exclude.
+ ///
+ /// The root directories to search in.
+ /// The repository paths to exclude from the search.
+ /// The repository names to exclude from the search.
public DirectorySearcherBase(IList rootDirectories, IList excludePaths, IList excludeNames)
{
RootDirectories = rootDirectories.AsReadOnly();
ExcludePaths = excludePaths.AsReadOnly();
ExcludeNames = excludeNames.AsReadOnly();
_nameMatcher.AddIncludePatterns(excludeNames);
+ SearchResults = _searchResults;
}
+
+ ///
+ /// Gets the enumeration options for the search.
+ ///
protected abstract EnumerationOptions EnumerationOptions { get; }
///
@@ -52,5 +68,5 @@ protected bool IsMatchExclude(DirectoryInfo directoryInfo)
);
}
- public abstract ParallelQuery Search(CancellationToken cancellationToken);
+ public abstract Task Search(CancellationToken cancellationToken);
}
diff --git a/local-repository-listing/Searcher/ISearcher.cs b/local-repository-listing/Searcher/ISearcher.cs
index 8befc92..003fdc4 100644
--- a/local-repository-listing/Searcher/ISearcher.cs
+++ b/local-repository-listing/Searcher/ISearcher.cs
@@ -1,9 +1,16 @@
-namespace LocalRepositoryListing.Searcher;
+using R3;
+namespace LocalRepositoryListing.Searcher;
///
/// Represents a searcher that can search for repositories based on specified criteria.
///
public interface ISearcher
{
+
+ ///
+ /// Gets the observable collection of search results.
+ ///
+ public Observable SearchResults { get; }
+
///
/// Gets the directory names to exclude from the search.
///
@@ -13,6 +20,6 @@ public interface ISearcher
/// Searches for repositories based on the specified criteria.
///
/// A cancellation token to cancel the search operation.
- /// A parallel query of objects representing the search results.
- public ParallelQuery Search(CancellationToken cancellationToken);
+ /// A task representing the asynchronous operation.
+ public Task Search(CancellationToken cancellationToken);
}
diff --git a/local-repository-listing/Searcher/NonRecursiveRepositorySearcher.cs b/local-repository-listing/Searcher/NonRecursiveRepositorySearcher.cs
index c3e2ef3..ba69824 100644
--- a/local-repository-listing/Searcher/NonRecursiveRepositorySearcher.cs
+++ b/local-repository-listing/Searcher/NonRecursiveRepositorySearcher.cs
@@ -1,4 +1,6 @@
-namespace LocalRepositoryListing.Searcher;
+using R3;
+
+namespace LocalRepositoryListing.Searcher;
///
/// Represents a class that searches for local repositories within specified root directories.
@@ -30,14 +32,19 @@ public class NonRecursiveRepositorySearcher(IList rootDirectories, IList
/// An array of root directories to search for repositories.
public NonRecursiveRepositorySearcher(IList rootDirectories) : this(rootDirectories, [], []) { }
- public override ParallelQuery Search(CancellationToken cancellationToken)
+ public override Task Search(CancellationToken cancellationToken)
{
- return RootDirectories
- .AsParallel()
- .WithCancellation(cancellationToken)
- .SelectMany(d => Directory.EnumerateDirectories(d, _rootSearchPattern, EnumerationOptions))
- .Select(d => new DirectoryInfo(d))
- .Where(d => d.GetDirectories(_searchPattern, SearchOption.TopDirectoryOnly).Length != 0)
- .Where(d => !IsMatchExclude(d));
+ // Somehow cancelled or exited faster by wrapping in Task.Run
+ return Task.Run(() =>
+ RootDirectories
+ .AsParallel()
+ .WithCancellation(cancellationToken)
+ .SelectMany(d => Directory.EnumerateDirectories(d, _rootSearchPattern, EnumerationOptions))
+ .Select(d => new DirectoryInfo(d))
+ .Where(d => d.GetDirectories(_searchPattern, SearchOption.TopDirectoryOnly).Length != 0)
+ .Where(d => !IsMatchExclude(d))
+ .ForAll(_searchResults.OnNext)
+ , cancellationToken);
+
}
}
diff --git a/local-repository-listing/Searcher/RecursiveRepositorySearcher.cs b/local-repository-listing/Searcher/RecursiveRepositorySearcher.cs
index 43a5da3..8e80578 100644
--- a/local-repository-listing/Searcher/RecursiveRepositorySearcher.cs
+++ b/local-repository-listing/Searcher/RecursiveRepositorySearcher.cs
@@ -48,19 +48,23 @@ public RecursiveRepositorySearcher(IList rootDirectories) : this(rootDir
///
public RecursiveRepositorySearcher() : this(Environment.GetLogicalDrives()) { }
- public override ParallelQuery Search(CancellationToken cancellationToken)
+ public override Task Search(CancellationToken cancellationToken)
{
- return RootDirectories
- .AsParallel()
- .WithCancellation(cancellationToken)
- .SelectMany(d => Directory.EnumerateDirectories(d, _rootSearchPattern, _rootEnumerationOptions))
- .AsParallel() // Somehow faster with this additional AsParallel()
- .WithCancellation(cancellationToken)
- .Where(d => !IsMatchExclude(new DirectoryInfo(d)))
- .SelectMany(d => Directory.EnumerateDirectories(d, _searchPattern, EnumerationOptions))
- .Select(d => Directory.GetParent(d))
- .Where(d => d != null)
- .Select(d => d ?? throw new InvalidOperationException("Directory.GetParent returns null"))
- .Where(d => !IsMatchExclude(d));
+ // Somehow Cancelled faster by wrapping in Task.Run
+ return Task.Run(() =>
+ RootDirectories
+ .AsParallel()
+ .WithCancellation(cancellationToken)
+ .SelectMany(d => Directory.EnumerateDirectories(d, _rootSearchPattern, _rootEnumerationOptions))
+ .AsParallel() // Somehow faster with this additional AsParallel()
+ .WithCancellation(cancellationToken)
+ .Where(d => !IsMatchExclude(new DirectoryInfo(d)))
+ .SelectMany(d => Directory.EnumerateDirectories(d, _searchPattern, EnumerationOptions))
+ .Select(d => Directory.GetParent(d))
+ .Where(d => d != null)
+ .Select(d => d ?? throw new InvalidOperationException("Directory.GetParent returns null"))
+ .Where(d => !IsMatchExclude(d))
+ .ForAll(_searchResults.OnNext)
+ , cancellationToken);
}
}
diff --git a/local-repository-listing/local-repository-listing.csproj b/local-repository-listing/local-repository-listing.csproj
index 5f1362a..242d15d 100644
--- a/local-repository-listing/local-repository-listing.csproj
+++ b/local-repository-listing/local-repository-listing.csproj
@@ -9,13 +9,14 @@
lepol
1.0.1
true
- true
+ true
embedded
+
From d23a7e419e30ea828e63cfb4dd1c04a2a48702b1 Mon Sep 17 00:00:00 2001
From: Gs-itisitcat <66198929+Gs-itisitcat@users.noreply.github.com>
Date: Thu, 23 May 2024 00:30:15 +0900
Subject: [PATCH 3/3] Update version to 1.1.0
---
local-repository-listing/local-repository-listing.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/local-repository-listing/local-repository-listing.csproj b/local-repository-listing/local-repository-listing.csproj
index 242d15d..4e1ca3f 100644
--- a/local-repository-listing/local-repository-listing.csproj
+++ b/local-repository-listing/local-repository-listing.csproj
@@ -7,7 +7,7 @@
enable
enable
lepol
- 1.0.1
+ 1.1.0
true
true
embedded