Skip to content

Commit

Permalink
TreeView from git cli
Browse files Browse the repository at this point in the history
  • Loading branch information
huntercfreeman committed May 2, 2024
1 parent 052ee9b commit a178956
Show file tree
Hide file tree
Showing 14 changed files with 347 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ public Effector(
_fileSystemProvider = fileSystemProvider;
}

/// <summary>
/// TODO: This method makes use of <see cref="IThrottle"/> and yet is accessing...
/// ...searchEffect.CancellationToken.
/// The issue here is that the search effect parameter to this method
/// could be out of date by the time that the throttle delay is completed.
/// This should be fixed. (2024-05-02)
/// </summary>
/// <param name="searchEffect"></param>
/// <param name="dispatcher"></param>
/// <returns></returns>
[EffectMethod]
public Task HandleSearchEffect(
SearchEffect searchEffect,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@using Luthetus.Common.RazorLib.Menus.Displays

@{ var localMenuRecord = _menuRecord; }

@if (localMenuRecord is null)
{
// TODO: Should something be displayed while the MenuRecord is loading?
}
else
{
<CascadingValue Name="ReturnFocusToParentFuncAsync" Value="TreeViewCommandArgs.RestoreFocusToTreeView.Invoke">
<MenuDisplay MenuRecord="localMenuRecord" />
</CascadingValue>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using Fluxor;
using Microsoft.AspNetCore.Components;
using System.Collections.Immutable;
using Luthetus.Ide.RazorLib.Terminals.States;
using Luthetus.Common.RazorLib.Commands.Models;
using Luthetus.Common.RazorLib.Menus.Models;
using Luthetus.Common.RazorLib.Dropdowns.Models;
using Luthetus.Common.RazorLib.Keys.Models;
using Luthetus.Common.RazorLib.Dimensions.Models;
using Luthetus.Ide.RazorLib.TreeViewImplementations.Models;
using Luthetus.Ide.RazorLib.CommandLines.Models;
using Luthetus.Ide.RazorLib.Terminals.Models;
using Luthetus.Common.RazorLib.BackgroundTasks.Models;
using System.Text;
using Luthetus.Ide.RazorLib.Gits.States;

namespace Luthetus.Ide.RazorLib.Gits.Displays;

public partial class GitChangesContextMenu : ComponentBase
{
[Inject]
private IState<TerminalState> TerminalStateWrap { get; set; } = null!;
[Inject]
private IBackgroundTaskService BackgroundTaskService { get; set; } = null!;

[CascadingParameter]
public GitState GitState { get; set; } = null!;

[Parameter, EditorRequired]
public TreeViewCommandArgs TreeViewCommandArgs { get; set; } = null!;

public static readonly Key<DropdownRecord> ContextMenuEventDropdownKey = Key<DropdownRecord>.NewKey();

private MenuRecord? _menuRecord = null;

protected override async Task OnInitializedAsync()
{
// Usage of 'OnInitializedAsync' lifecycle method ensure the context menu is only rendered once.
// Otherwise, one might have the context menu's options change out from under them.
_menuRecord = await GetMenuRecord(TreeViewCommandArgs);
await InvokeAsync(StateHasChanged);

await base.OnInitializedAsync();
}

private async Task<MenuRecord> GetMenuRecord(TreeViewCommandArgs commandArgs, bool isRecursiveCall = false)
{
if (!isRecursiveCall && commandArgs.TreeViewContainer.SelectedNodeList.Count > 1)
{
return await GetMultiSelectionMenuRecord(commandArgs);
}

if (commandArgs.NodeThatReceivedMouseEvent is null)
return MenuRecord.Empty;

var menuRecordsList = new List<MenuOptionRecord>();

if (!menuRecordsList.Any())
return MenuRecord.Empty;

return new MenuRecord(menuRecordsList.ToImmutableArray());
}

private async Task<MenuRecord> GetMultiSelectionMenuRecord(TreeViewCommandArgs commandArgs)
{
var menuOptionRecordList = new List<MenuOptionRecord>();
Func<Task> runAllOnClicksWithinSelection = () => Task.CompletedTask;
bool runAllOnClicksWithinSelectionHasEffect = false;

if (!menuOptionRecordList.Any())
return MenuRecord.Empty;

return new MenuRecord(menuOptionRecordList.ToImmutableArray());
}


public static string GetContextMenuCssStyleString(TreeViewCommandArgs? commandArgs)
{
if (commandArgs?.ContextMenuFixedPosition is null)
return "display: none;";

var left =
$"left: {commandArgs.ContextMenuFixedPosition.LeftPositionInPixels.ToCssValue()}px;";

var top =
$"top: {commandArgs.ContextMenuFixedPosition.TopPositionInPixels.ToCssValue()}px;";

return $"{left} {top}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@using Luthetus.Common.RazorLib.Dropdowns.Displays
@using Luthetus.Common.RazorLib.Dropdowns.Models
@using Luthetus.Common.RazorLib.TreeViews.Displays
@using Luthetus.Common.RazorLib.TreeViews.Models
@using Luthetus.Ide.RazorLib.Terminals.Models
@using System.Collections.Immutable

<div class="luth_ide_test-explorer-tree-view"
style="height: 100%;">

@{ var appOptionsState = AppOptionsStateWrap.Value; }

<CascadingValue Name="LuthetusTreeViewIconWidth" Value="appOptionsState.Options.IconSizeInPixels">
<CascadingValue Name="LuthetusTreeViewIconHeight" Value="appOptionsState.Options.IconSizeInPixels">
<CascadingValue Name="OffsetPerDepthInPixels" Value="OffsetPerDepthInPixels">
<TreeViewContainerDisplay TreeViewContainerKey="Gits.States.GitState.TreeViewGitChangesKey"
CssStyleString="height: 100%;"
OnContextMenuFunc="OnTreeViewContextMenuFunc"
TreeViewKeyboardEventHandler="_treeViewKeyboardEventHandler"
TreeViewMouseEventHandler="_treeViewMouseEventHandler" />
</CascadingValue>
</CascadingValue>
</CascadingValue>

<DropdownDisplay DropdownKey="GitChangesContextMenu.ContextMenuEventDropdownKey"
DropdownPositionKind="DropdownPositionKind.Unset"
CssStyleString="@GitChangesContextMenu.GetContextMenuCssStyleString(_mostRecentTreeViewCommandArgs)">

<GitChangesContextMenu TreeViewCommandArgs="_mostRecentTreeViewCommandArgs" />
</DropdownDisplay>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using Fluxor;
using Luthetus.Common.RazorLib.BackgroundTasks.Models;
using Luthetus.Common.RazorLib.Commands.Models;
using Luthetus.Common.RazorLib.Dropdowns.States;
using Luthetus.Common.RazorLib.Options.States;
using Luthetus.Common.RazorLib.TreeViews.Models;
using Luthetus.Ide.RazorLib.Gits.States;
using Microsoft.AspNetCore.Components;

namespace Luthetus.Ide.RazorLib.Gits.Displays;

public partial class GitChangesTreeViewDisplay : ComponentBase
{
[Inject]
private ITreeViewService TreeViewService { get; set; } = null!;
[Inject]
private IDispatcher Dispatcher { get; set; } = null!;
[Inject]
private IState<AppOptionsState> AppOptionsStateWrap { get; set; } = null!;
[Inject]
private IBackgroundTaskService BackgroundTaskService { get; set; } = null!;

[CascadingParameter]
public GitState GitState { get; set; } = null!;

private TreeViewCommandArgs? _mostRecentTreeViewCommandArgs;
private TreeViewKeyboardEventHandler _treeViewKeyboardEventHandler = null!;
private TreeViewMouseEventHandler _treeViewMouseEventHandler = null!;

private int OffsetPerDepthInPixels => (int)Math.Ceiling(
AppOptionsStateWrap.Value.Options.IconSizeInPixels * (2.0 / 3.0));

protected override void OnInitialized()
{
_treeViewKeyboardEventHandler = new TreeViewKeyboardEventHandler(
TreeViewService,
BackgroundTaskService);

_treeViewMouseEventHandler = new TreeViewMouseEventHandler(
TreeViewService,
BackgroundTaskService);

base.OnInitialized();
}

private async Task OnTreeViewContextMenuFunc(TreeViewCommandArgs treeViewCommandArgs)
{
_mostRecentTreeViewCommandArgs = treeViewCommandArgs;

// The order of 'StateHasChanged(...)' and 'AddActiveDropdownKey(...)' is important.
// The ChildContent renders nothing, unless the provider of the child content
// re-renders now that there is a given '_mostRecentTreeViewContextMenuCommandArgs'
await InvokeAsync(StateHasChanged);

Dispatcher.Dispatch(new DropdownState.AddActiveAction(
GitChangesContextMenu.ContextMenuEventDropdownKey));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,5 @@
Status
</button>

<div>
@foreach (var gitFile in localGitState.GitFileList)
{
<div>@gitFile.AbsolutePath.Value|@gitFile.GitDirtyReason</div>
}
</div>
<GitChangesTreeViewDisplay/>
</div>
10 changes: 9 additions & 1 deletion Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Actions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
namespace Luthetus.Ide.RazorLib.Gits.States;
using Luthetus.Common.RazorLib.FileSystems.Models;
using Luthetus.Ide.RazorLib.Gits.Models;
using System.Collections.Immutable;

namespace Luthetus.Ide.RazorLib.Gits.States;

public partial record GitState
{
/// <summary>
/// If the expected path is not the actual path, then the git file list will NOT be changed.
/// </summary>
public record SetGitFileListAction(IAbsolutePath ExpectedGitFolderAbsolutePath, ImmutableList<GitFile> GitFileList);
public record SetGitStateWithAction(Func<GitState, GitState> GitStateWithFunc);
}
77 changes: 77 additions & 0 deletions Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Effector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using Fluxor;
using Luthetus.Common.RazorLib.Reactives.Models;
using Luthetus.Common.RazorLib.TreeViews.Models;
using Luthetus.Ide.RazorLib.ComponentRenderers.Models;
using Luthetus.Ide.RazorLib.TreeViewImplementations.Models;
using System.Collections.Immutable;

namespace Luthetus.Ide.RazorLib.Gits.States;

public partial record GitState
{
public class Effector
{
private readonly IState<GitState> _gitStateWrap;
private readonly ILuthetusIdeComponentRenderers _ideComponentRenderers;
private readonly ITreeViewService _treeViewService;
private readonly IThrottle _throttle = new Throttle(TimeSpan.FromMilliseconds(300));

public Effector(
IState<GitState> gitStateWrap,
ILuthetusIdeComponentRenderers ideComponentRenderers,
ITreeViewService treeViewService)
{
_gitStateWrap = gitStateWrap;
_ideComponentRenderers = ideComponentRenderers;
_treeViewService = treeViewService;
}

[EffectMethod(typeof(SetGitFileListAction))]
public Task HandleSetGitStateWithAction(IDispatcher dispatcher)
{
// Suppress unused variable warning
_ = dispatcher;

_throttle.PushEvent(_ =>
{
var gitState = _gitStateWrap.Value;

var treeViewList = gitState.GitFileList.Select(x => new TreeViewGitFile(
x,
_ideComponentRenderers,
false,
false))
.ToArray();

var adhocRoot = TreeViewAdhoc.ConstructTreeViewAdhoc(treeViewList);
var firstNode = treeViewList.FirstOrDefault();

var activeNodes = firstNode is null
? Array.Empty<TreeViewNoType>()
: new[] { firstNode };

if (!_treeViewService.TryGetTreeViewContainer(TreeViewGitChangesKey, out var container))
{
_treeViewService.RegisterTreeViewContainer(new TreeViewContainer(
TreeViewGitChangesKey,
adhocRoot,
activeNodes.ToImmutableList()));
}
else
{
_treeViewService.SetRoot(TreeViewGitChangesKey, adhocRoot);

_treeViewService.SetActiveNode(
TreeViewGitChangesKey,
firstNode,
true,
false);
}

return Task.CompletedTask;
});

return Task.CompletedTask;
}
}
}
12 changes: 4 additions & 8 deletions Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Main.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
using Fluxor;
using Luthetus.Common.RazorLib.FileSystems.Models;
using Luthetus.Common.RazorLib.Keys.Models;
using Luthetus.Common.RazorLib.TreeViews.Models;
using Luthetus.Ide.RazorLib.Gits.Models;
using System.Collections.Immutable;

namespace Luthetus.Ide.RazorLib.Gits.States;

/// <summary>
/// The Folder, ".git" may be in the following locations:<br/>
/// -In the context of .NET:<br/>
/// --The folder containing the user selected .NET Solution<br/>
/// --The folder containing the user selected C# Project which is being contained in an adhoc .NET Solution<br/>
/// -In the context of using the folder explorer<br/>
/// --The folder which is user selected.<br/>
/// </summary>
[FeatureState]
public partial record GitState(
IAbsolutePath? GitFolderAbsolutePath,
ImmutableList<GitFile> GitFileList,
ImmutableList<GitTask> ActiveGitTasks)
{
public static readonly Key<TreeViewContainer> TreeViewGitChangesKey = Key<TreeViewContainer>.NewKey();

public GitState() : this(null, ImmutableList<GitFile>.Empty, ImmutableList<GitTask>.Empty)
{

Expand Down
22 changes: 20 additions & 2 deletions Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Reducer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,30 @@ public partial record GitState
{
public class Reducer
{
[ReducerMethod]
public static GitState ReduceSetGitFileListAction(
GitState inState,
SetGitFileListAction setGitFileListAction)
{
if (inState.GitFolderAbsolutePath != setGitFileListAction.ExpectedGitFolderAbsolutePath)
{
// Git folder was changed while the text was being parsed,
// throw away the result since it is thereby invalid.
return inState;
}

return inState with
{
GitFileList = setGitFileListAction.GitFileList
};
}

[ReducerMethod]
public static GitState ReduceSetGitStateWithAction(
GitState inGitState,
GitState inState,
SetGitStateWithAction setGitStateWithAction)
{
return setGitStateWithAction.GitStateWithFunc.Invoke(inGitState);
return setGitStateWithAction.GitStateWithFunc.Invoke(inState);
}
}
}
Loading

0 comments on commit a178956

Please sign in to comment.