From 915c1977e2eba79c77909b31156055a517bbe2cf Mon Sep 17 00:00:00 2001 From: Coen van den Munckhof Date: Sun, 10 Sep 2023 23:04:02 +0200 Subject: [PATCH] Fix deferred --- src/RepoM.Api/Git/RepositoryAction.cs | 58 ++++++++++++++++++- .../ActionAssociateFileV1Mapper.cs | 2 +- .../ActionBrowseRepositoryV1Mapper.cs | 8 +-- .../ActionMappers/ActionFolderV1Mapper.cs | 18 +++--- .../ActionGitCheckoutV1Mapper.cs | 4 +- .../Data/Actions/RepositoryActionFolderV1.cs | 10 ++-- .../RepoMVariableProvider.cs | 6 +- .../Variables/RepoMVariableProviderStore.cs | 6 ++ src/RepoM.Api/IO/Variables/Scope.cs | 6 ++ src/RepoM.App/MainWindow.xaml.cs | 17 ++---- 10 files changed, 98 insertions(+), 37 deletions(-) diff --git a/src/RepoM.Api/Git/RepositoryAction.cs b/src/RepoM.Api/Git/RepositoryAction.cs index 86d642ab..6378aa91 100644 --- a/src/RepoM.Api/Git/RepositoryAction.cs +++ b/src/RepoM.Api/Git/RepositoryAction.cs @@ -2,6 +2,7 @@ namespace RepoM.Api.Git; using System; using System.Collections.Generic; +using RepoM.Api.IO.Variables; using RepoM.Core.Plugin.Repository; using RepoM.Core.Plugin.RepositoryActions; using RepoM.Core.Plugin.RepositoryActions.Actions; @@ -14,6 +15,61 @@ public RepositorySeparatorAction(IRepository repository) } } +public class DeferredRepositoryAction : RepositoryAction +{ + private readonly Func? _action; + private readonly Dictionary _envVars; + private readonly Scope? _scope; + private IRepository _repository; + + public DeferredRepositoryAction(string name, IRepository repository, bool captureScope) + : base(name, repository) + { + _repository = repository; + + if (captureScope) + { + _envVars = EnvironmentVariableStore.Get(_repository); + _scope = RepoMVariableProviderStore.VariableScope.Value?.Clone(); + } + } + + public Func? DeferredSubActionsEnumerator + { + get + { + if (_action == null) + { + return null; + } + + return () => + { + using IDisposable _ = EnvironmentVariableStore.Set(_envVars); + using IDisposable __ = _scope == null ? CreateDummy() : RepoMVariableProviderStore.Set(_scope); + return _action.Invoke(); + }; + } + init + { + _action = value; + } + } + + private static IDisposable CreateDummy() + { + return new DummyIDisposable(); + } + + private sealed class DummyIDisposable : IDisposable + { + public void Dispose() + { + // intentionally do nothing + } + } +} + public class RepositoryAction : RepositoryActionBase { public RepositoryAction(string name, IRepository repository): @@ -40,7 +96,5 @@ protected RepositoryActionBase(IRepository repository) public bool CanExecute { get; init; } = true; - public Func? DeferredSubActionsEnumerator { get; init; } - public IEnumerable? SubActions { get; init; } } \ No newline at end of file diff --git a/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionAssociateFileV1Mapper.cs b/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionAssociateFileV1Mapper.cs index 195d80ec..0df6cfc7 100644 --- a/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionAssociateFileV1Mapper.cs +++ b/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionAssociateFileV1Mapper.cs @@ -67,7 +67,7 @@ IEnumerable IActionToRepositoryActionMapper.Map(Repository return null; } - return new Git.RepositoryAction(actionName, repository) + return new Git.DeferredRepositoryAction(actionName, repository, false) { DeferredSubActionsEnumerator = () => GetFiles(repository, filePattern) diff --git a/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionBrowseRepositoryV1Mapper.cs b/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionBrowseRepositoryV1Mapper.cs index 11b06d3a..2d6e6343 100644 --- a/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionBrowseRepositoryV1Mapper.cs +++ b/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionBrowseRepositoryV1Mapper.cs @@ -70,12 +70,12 @@ private IEnumerable Map(RepositoryActionBrowseRepositoryV1? ac return CreateProcessRunnerAction(actionName, repository.Remotes[0].Url, repository); } - return new RepositoryAction(actionName, repository) + return new DeferredRepositoryAction(actionName, repository, false) { DeferredSubActionsEnumerator = () => repository.Remotes - .Take(50) - .Select(remote => CreateProcessRunnerAction(remote.Name, remote.Url, repository)) - .ToArray(), + .Take(50) + .Select(remote => CreateProcessRunnerAction(remote.Name, remote.Url, repository)) + .ToArray(), }; } diff --git a/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionFolderV1Mapper.cs b/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionFolderV1Mapper.cs index 2bdbb561..19645048 100644 --- a/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionFolderV1Mapper.cs +++ b/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionFolderV1Mapper.cs @@ -27,7 +27,7 @@ IEnumerable IActionToRepositoryActionMapper.Map(Repository return Map(action as RepositoryActionFolderV1, repository, actionMapperComposition); } - private IEnumerable Map(RepositoryActionFolderV1? action, Repository repository, ActionMapperComposition actionMapperComposition) + private IEnumerable Map(RepositoryActionFolderV1? action, Repository repository, ActionMapperComposition actionMapperComposition) { if (action == null) { @@ -49,25 +49,25 @@ IEnumerable IActionToRepositoryActionMapper.Map(Repository if (deferred) { - yield return new RepoM.Api.Git.RepositoryAction(name, repository) + yield return new DeferredRepositoryAction(name, repository, true) { CanExecute = true, DeferredSubActionsEnumerator = () => action.Items - .Where(x => _expressionEvaluator.EvaluateBooleanExpression(x.Active, repository)) - .SelectMany(x => actionMapperComposition.Map(x, repository)) - .ToArray(), + .Where(repoAction => _expressionEvaluator.EvaluateBooleanExpression(repoAction.Active, repository)) + .SelectMany(repoAction => actionMapperComposition.Map(repoAction, repository)) + .ToArray(), }; } else { - yield return new RepoM.Api.Git.RepositoryAction(name, repository) + yield return new Git.RepositoryAction(name, repository) { CanExecute = true, SubActions = action.Items - .Where(x => _expressionEvaluator.EvaluateBooleanExpression(x.Active, repository)) - .SelectMany(x => actionMapperComposition.Map(x, repository)) - .ToArray(), + .Where(repoAction => _expressionEvaluator.EvaluateBooleanExpression(repoAction.Active, repository)) + .SelectMany(repoAction => actionMapperComposition.Map(repoAction, repository)) + .ToArray(), }; } } diff --git a/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionGitCheckoutV1Mapper.cs b/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionGitCheckoutV1Mapper.cs index 34cf2da1..6ab17929 100644 --- a/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionGitCheckoutV1Mapper.cs +++ b/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/ActionMappers/ActionGitCheckoutV1Mapper.cs @@ -52,7 +52,7 @@ private IEnumerable Map(RepositoryActionGitCheckoutV1? act name = _translationService.Translate("Checkout"); } - yield return new Git.RepositoryAction(name, repository) + yield return new DeferredRepositoryAction(name, repository, false) { DeferredSubActionsEnumerator = () => repository.LocalBranches @@ -65,7 +65,7 @@ private IEnumerable Map(RepositoryActionGitCheckoutV1? act .Union(new RepositoryActionBase[] { new RepositorySeparatorAction(repository), // doesn't work todo - new Git.RepositoryAction(_translationService.Translate("Remote branches"), repository) + new DeferredRepositoryAction(_translationService.Translate("Remote branches"), repository, false) { DeferredSubActionsEnumerator = () => { diff --git a/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/Data/Actions/RepositoryActionFolderV1.cs b/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/Data/Actions/RepositoryActionFolderV1.cs index 2170d3c9..411ec41b 100644 --- a/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/Data/Actions/RepositoryActionFolderV1.cs +++ b/src/RepoM.Api/IO/ModuleBasedRepositoryActionProvider/Data/Actions/RepositoryActionFolderV1.cs @@ -22,9 +22,11 @@ public sealed class RepositoryActionFolderV1 : RepositoryAction [PropertyType(typeof(List))] public List Items { get; set; } = new List(); - // This property has no xml documentation because then it is skipped when generating the documentation. - // IsDeferred is used to indicate that the items in the folder are genereted lazy. This is used to improve performance. - // The problem is that variables at this deferred geneartion are not available anymore resulting in unwanted behaviour. - // At this moment, IsDeferred is not suggested to be used. + /// + /// Menu is deferred. This will speed up visualisation. + /// + [EvaluatedProperty] + [PropertyType(typeof(bool))] + [PropertyDefaultBoolValue(false)] public string? IsDeferred { get; set; } } \ No newline at end of file diff --git a/src/RepoM.Api/IO/VariableProviders/RepoMVariableProvider.cs b/src/RepoM.Api/IO/VariableProviders/RepoMVariableProvider.cs index 066edad1..32ae1d11 100644 --- a/src/RepoM.Api/IO/VariableProviders/RepoMVariableProvider.cs +++ b/src/RepoM.Api/IO/VariableProviders/RepoMVariableProvider.cs @@ -130,6 +130,9 @@ public bool CanProvide(string key) envSearchKey = envKey[..index]; } + var ph = new PropertyHandler(); + var ah = new ArrayHandler(); + Scope? scope = RepoMVariableProviderStore.VariableScope.Value; while (true) @@ -149,9 +152,6 @@ public bool CanProvide(string key) IEnumerable selectors = FindSelectors(envKey[index..]); var r = result; - var ph = new PropertyHandler(); - var ah = new ArrayHandler(); - foreach (IItem selector in selectors) { if (selector is PropertySelector ps) diff --git a/src/RepoM.Api/IO/Variables/RepoMVariableProviderStore.cs b/src/RepoM.Api/IO/Variables/RepoMVariableProviderStore.cs index d73c590d..b866ae37 100644 --- a/src/RepoM.Api/IO/Variables/RepoMVariableProviderStore.cs +++ b/src/RepoM.Api/IO/Variables/RepoMVariableProviderStore.cs @@ -14,4 +14,10 @@ public static IDisposable Push(List vars) VariableScope.Value = new Scope(VariableScope.Value, vars); return VariableScope.Value; } + + public static IDisposable Set(Scope scope) + { + VariableScope.Value = scope; + return VariableScope.Value; + } } \ No newline at end of file diff --git a/src/RepoM.Api/IO/Variables/Scope.cs b/src/RepoM.Api/IO/Variables/Scope.cs index e1769fee..dbe1339b 100644 --- a/src/RepoM.Api/IO/Variables/Scope.cs +++ b/src/RepoM.Api/IO/Variables/Scope.cs @@ -35,4 +35,10 @@ public void Dispose() _isDisposed = true; } } + + public Scope Clone() + { + // this is not a real clone as it uses a reference to Variables. this is ok. + return new Scope(Parent?.Clone(), Variables); + } } \ No newline at end of file diff --git a/src/RepoM.App/MainWindow.xaml.cs b/src/RepoM.App/MainWindow.xaml.cs index b05798f6..d41bea0e 100644 --- a/src/RepoM.App/MainWindow.xaml.cs +++ b/src/RepoM.App/MainWindow.xaml.cs @@ -216,7 +216,7 @@ private bool LstRepositoriesContextMenuOpening(object sender, ContextMenu ctxMen items.Add(new Separator()); } } - else if (action is RepositoryAction _) + else { Control? controlItem = CreateMenuItem(sender, action, vm); if (controlItem != null) @@ -224,11 +224,6 @@ private bool LstRepositoriesContextMenuOpening(object sender, ContextMenu ctxMen items.Add(controlItem); } } - else - { - // do nothing. - // log? - } } return true; @@ -418,7 +413,7 @@ private void ShowUpdateIfAvailable() Action clickAction = (object clickSender, object clickArgs) => { - if (repositoryAction?.Action == null || repositoryAction.Action is NullAction) + if (repositoryAction?.Action is null or NullAction) { return; } @@ -446,7 +441,7 @@ private void ShowUpdateIfAvailable() // this is a deferred submenu. We want to make sure that the context menu can pop up // fast, while submenus are not evaluated yet. We don't want to make the context menu // itself slow because the creation of the submenu items takes some time. - if (repositoryAction?.DeferredSubActionsEnumerator != null) + if (repositoryAction is DeferredRepositoryAction deferredRepositoryAction && deferredRepositoryAction.DeferredSubActionsEnumerator != null) { // this is a template submenu item to enable submenus under the current // menu item. this item gets removed when the real subitems are created @@ -457,7 +452,7 @@ void SelfDetachingEventHandler(object _, RoutedEventArgs evtArgs) item.SubmenuOpened -= SelfDetachingEventHandler; item.Items.Clear(); - foreach (RepositoryActionBase subAction in repositoryAction.DeferredSubActionsEnumerator()) + foreach (RepositoryActionBase subAction in deferredRepositoryAction.DeferredSubActionsEnumerator()) { Control? controlItem = CreateMenuItem(sender, subAction); if (controlItem != null) @@ -465,13 +460,11 @@ void SelfDetachingEventHandler(object _, RoutedEventArgs evtArgs) item.Items.Add(controlItem); } } - - Console.WriteLine($"Added {item.Items.Count} deferred sub action(s)."); } item.SubmenuOpened += SelfDetachingEventHandler; } - else if (repositoryAction?.SubActions != null) + else if (repositoryAction.SubActions != null) { foreach (RepositoryActionBase subAction in repositoryAction.SubActions) {