Skip to content

Commit

Permalink
Monitor repository folder
Browse files Browse the repository at this point in the history
Handle changes to the repository folder as they happen, rather than rebuilding
the entire menu evey time it's opened.
  • Loading branch information
Scepheo committed Jun 17, 2019
1 parent 789f822 commit 65bb325
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 102 deletions.
3 changes: 3 additions & 0 deletions src/GitMan/Config/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ public static Settings Load()
settings.Save();
}

settings.AzureProviders ??= Array.Empty<AzureProviderSettings>();
settings.GitHubProviders ??= Array.Empty<GitHubProviderSettings>();

return settings;
}

Expand Down
81 changes: 63 additions & 18 deletions src/GitMan/Context.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,12 @@ public class Context : ApplicationContext
private readonly Settings _settings;
private readonly RepositoryAction[] _repositoryActions;
private readonly RemoteProvider[] _remoteProviders;
private readonly RepositoryDirectory _repositoryDirectory;

private static void EmptyHandler(object sender, EventArgs eventArgs) { }

public Context()
{
_icon = new NotifyIcon();
_icon.DoubleClick += Icon_DoubleClick;
_icon.MouseDown += Icon_MouseDown;
_icon.Visible = true;
_icon.Icon = LoadIcon();

_main = new Main();

_settings = Settings.Load();
Expand All @@ -40,6 +35,51 @@ public Context()
var gitHubProviders = _settings.GitHubProviders
.Select(provider => (RemoteProvider)new GitHubProvider(provider));
_remoteProviders = azureProviders.Concat(gitHubProviders).ToArray();

var directoryInfo = new DirectoryInfo(_settings.RepositoryFolder);
_repositoryDirectory = new RepositoryDirectory(directoryInfo);
_repositoryDirectory.Added += RepositoryAdded;
_repositoryDirectory.Removed += RepositoryRemoved;
_repositoryDirectory.Renamed += RepositoryRenamed;

_icon = new NotifyIcon();
_icon.DoubleClick += Icon_DoubleClick;
_icon.Visible = true;
_icon.Icon = LoadIcon();
_icon.ContextMenu = MakeContextMenu();
}

private void InsertMenuItem(MenuItem menuItem)
{
var menuItems = _icon.ContextMenu.MenuItems;

var currentItems = menuItems.Cast<MenuItem>();
var newItems = Enumerable.Repeat(menuItem, 1);
var allitems = currentItems.Concat(newItems);
var orderedItems = allitems.OrderBy(item => item.Name).ToArray();

menuItems.Clear();
menuItems.AddRange(orderedItems);
}

private void RepositoryAdded(Repository repository)
{
var menuItem = MakeMenuItem(repository);
InsertMenuItem(menuItem);
}

private void RepositoryRemoved(Repository repository)
{
var currentItems = _icon.ContextMenu.MenuItems.Cast<MenuItem>();
var name = GetRepositoryItemName(repository);
var menuItem = currentItems.Single(item => item.Name == name);
_icon.ContextMenu.MenuItems.Remove(menuItem);
}

private void RepositoryRenamed(Repository oldRepository, Repository newRepository)
{
RepositoryRemoved(oldRepository);
RepositoryAdded(newRepository);
}

private void Icon_DoubleClick(object sender, EventArgs e)
Expand All @@ -62,25 +102,21 @@ private Icon LoadIcon()
return new Icon(resourceStream);
}

private void Icon_MouseDown(object sender, MouseEventArgs e)
private ContextMenu MakeContextMenu()
{
var directory = new DirectoryInfo(_settings.RepositoryFolder);
var repositoryList = RepositoryList.Load(directory);

var items = new List<MenuItem>();

var cloneItem = MakeCloneItem(repositoryList);
var cloneItem = MakeCloneItem(_repositoryDirectory);
items.Add(cloneItem);

var repositoryItems = repositoryList.Select(MakeMenuItem).ToArray();
var repositoryItems = _repositoryDirectory.Select(MakeMenuItem).ToArray();
items.AddRange(repositoryItems);

var exitItem = MakeExitItem();
items.Add(exitItem);

var menu = new ContextMenu(items.ToArray());

_icon.ContextMenu = menu;
return menu;
}

protected override void Dispose(bool disposing)
Expand All @@ -90,6 +126,11 @@ protected override void Dispose(bool disposing)
_icon.Dispose();
}

private string GetRepositoryItemName(Repository repository)
{
return $"2_REPO_{repository.Name}";
}

private MenuItem MakeMenuItem(Repository repository)
{
var name = repository.Name;
Expand All @@ -102,7 +143,11 @@ private MenuItem MakeMenuItem(Repository repository)
subItems.AddRange(repositoryAction.GetMenuItems(directoryInfo));
}

var menuItem = new MenuItem(name, subItems.ToArray());
var menuItem = new MenuItem(name, subItems.ToArray())
{
Name = GetRepositoryItemName(repository)
};

return menuItem;
}

Expand All @@ -114,10 +159,10 @@ void onClick(object sender, EventArgs eventArgs)
}

const string name = "Exit";
return new MenuItem(name, onClick);
return new MenuItem(name, onClick) { Name = "3_EXIT" };
}

private MenuItem MakeCloneItem(RepositoryList existingRepositories)
private MenuItem MakeCloneItem(RepositoryDirectory existingRepositories)
{
var items = new List<MenuItem>();

Expand All @@ -133,7 +178,7 @@ private MenuItem MakeCloneItem(RepositoryList existingRepositories)
const string name = "Clone";
var itemArray = items.ToArray();

return new MenuItem(name, itemArray);
return new MenuItem(name, itemArray) { Name = "1_CLONE" };
}
}
}
2 changes: 1 addition & 1 deletion src/GitMan/Providers/RemoteProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public RemoteProvider(string name, Dictionary<string, string> defaultConfig)

public MenuItem MakeRemoteProviderItem(
string repositoryFolder,
RepositoryList existingRepositories)
RepositoryDirectory existingRepositories)
{
var loadItem = new MenuItem("Loading...");
var dummyItems = new[] { loadItem };
Expand Down
36 changes: 5 additions & 31 deletions src/GitMan/Repository.cs
Original file line number Diff line number Diff line change
@@ -1,47 +1,21 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.IO;

namespace GitMan
{
internal class Repository
{
private readonly DirectoryInfo _directoryInfo;
public string Name { get; }
public string FullName { get; }

private Repository(DirectoryInfo directoryInfo)
{
_directoryInfo = directoryInfo;
Name = directoryInfo.Name;
FullName = directoryInfo.FullName;
}

public static Repository Load(DirectoryInfo directoryInfo)
{
return new Repository(directoryInfo);
}

public string Name => _directoryInfo.Name;

public string FullName => _directoryInfo.FullName;

public IEnumerable<FileInfo> SolutionFiles => _directoryInfo.EnumerateFiles("*.sln", SearchOption.AllDirectories);

public bool IsVsCodeProject()
{
var subDirectories = _directoryInfo.EnumerateDirectories();
var hasVsCodeDirectory = subDirectories.Any(IsVsCodeDirectory);
return hasVsCodeDirectory;
}

private static bool IsVsCodeDirectory(DirectoryInfo directoryInfo)
{
const string vsCodeDirectoryName = ".vscode";

var isVsCodeDirectory = string.Equals(
directoryInfo.Name,
vsCodeDirectoryName,
StringComparison.OrdinalIgnoreCase);

return isVsCodeDirectory;
}
}
}
152 changes: 152 additions & 0 deletions src/GitMan/RepositoryDirectory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace GitMan
{
internal class RepositoryDirectory : IDisposable, IEnumerable<Repository>
{
private readonly DirectoryInfo _directoryInfo;
private readonly Dictionary<string, Repository> _repositories;
private readonly FileSystemWatcher _watcher;

public RepositoryDirectory(DirectoryInfo directoryInfo)
{
_directoryInfo = directoryInfo;

_watcher = new FileSystemWatcher(directoryInfo.FullName);
_watcher.Changed += HandleChange;
_watcher.Created += HandleChange;
_watcher.Deleted += HandleChange;
_watcher.Renamed += HandleChange;
_watcher.EnableRaisingEvents = true;

_repositories = BuildIndex();
}

public delegate void RepositoryAddedHandler(Repository repository);
public delegate void RepositoryRenamedHandler(Repository oldRepository, Repository newRepository);
public delegate void RepositoryRemovedHandler(Repository repository);

public event RepositoryAddedHandler Added;
public event RepositoryRenamedHandler Renamed;
public event RepositoryRemovedHandler Removed;

private Dictionary<string, Repository> BuildIndex()
{
var index = new Dictionary<string, Repository>();

foreach (var subDirectoryInfo in _directoryInfo.EnumerateDirectories())
{
if (IsGitRepository(subDirectoryInfo))
{
var repository = Repository.Load(subDirectoryInfo);
index[subDirectoryInfo.Name] = repository;
}
}

return index;
}

private void HandleChange(object sender, FileSystemEventArgs eventArgs)
{
var oldName = eventArgs is RenamedEventArgs rename ? rename.OldName : eventArgs.Name;
var newName = eventArgs.Name;
HandleChange(oldName, newName);
}

private bool TryGetDirectory(string name, out DirectoryInfo directory)
{
directory = _directoryInfo.EnumerateDirectories().SingleOrDefault(dir => dir.Name == name);
return directory != default;
}

private bool WasRepository(string directoryName, out Repository repository)
{
if (directoryName == null)
{
repository = null;
return false;
}

return _repositories.TryGetValue(directoryName, out repository);
}

private bool IsRepository(string directoryName, out Repository repository)
{
if (!TryGetDirectory(directoryName, out var directory))
{
repository = null;
return false;
}

if (IsGitRepository(directory))
{
repository = Repository.Load(directory);
return true;
}

repository = null;
return false;
}

private void HandleChange(string oldDirectoryName, string newDirectoryName)
{
var wasRepository = WasRepository(oldDirectoryName, out var oldRepository);
var isRepository = IsRepository(newDirectoryName, out var newRepository);
var isNameChanged = !string.Equals(oldDirectoryName, newDirectoryName, StringComparison.OrdinalIgnoreCase);

if (wasRepository && isRepository)
{
if (isNameChanged)
{
_repositories.Remove(oldDirectoryName);
_repositories[newRepository.Name] = newRepository;
Renamed?.Invoke(oldRepository, newRepository);
}
}
else if (wasRepository)
{
_repositories.Remove(oldDirectoryName);
Removed?.Invoke(oldRepository);
}
else if (isRepository)
{
_repositories[newRepository.Name] = newRepository;
Added?.Invoke(newRepository);
}
}

public void Dispose()
{
_watcher.Dispose();
}

private static bool IsGitRepository(DirectoryInfo directoryInfo)
{
var subDirectories = directoryInfo.EnumerateDirectories();
var hasGitDirectory = subDirectories.Any(IsGitDirectory);
return hasGitDirectory;
}

private static bool IsGitDirectory(DirectoryInfo directoryInfo)
{
const string gitDirectoryName = ".git";

var isGitDirectory = string.Equals(
directoryInfo.Name,
gitDirectoryName,
StringComparison.OrdinalIgnoreCase);

return isGitDirectory;
}

public int Count => _repositories.Count;

public IEnumerator<Repository> GetEnumerator() => _repositories.Values.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
Loading

0 comments on commit 65bb325

Please sign in to comment.