diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..b18344d4b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "dotnet-test-explorer.testProjectPath": "/home/hunter/Repos/Luthetus.Ide_Fork/Source/Tests/Ide/Luthetus.Ide.Tests.csproj" +} \ No newline at end of file diff --git a/Luthetus.Ide.sln b/Luthetus.Ide.sln index 3f025484e..751a79c4c 100644 --- a/Luthetus.Ide.sln +++ b/Luthetus.Ide.sln @@ -41,8 +41,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luthetus.CompilerServices.L EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luthetus.CompilerServices.Lang.Razor", "Source\Lib\CompilerServices\Razor\Luthetus.CompilerServices.Lang.Razor.csproj", "{943CF0EB-80DF-4D2F-BF23-0EF6A5B2DA49}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luthetus.CompilerServices.Lang.Terminal", "Source\Lib\CompilerServices\Terminal\Luthetus.CompilerServices.Lang.Terminal.csproj", "{76F4AB6D-5249-4D0A-AAF8-D656E792AA34}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luthetus.CompilerServices.Lang.TypeScript", "Source\Lib\CompilerServices\TypeScript\Luthetus.CompilerServices.Lang.TypeScript.csproj", "{FA133D31-0DCC-4737-A76F-8AF6D8755EDE}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luthetus.CompilerServices.Lang.Xml", "Source\Lib\CompilerServices\Xml\Luthetus.CompilerServices.Lang.Xml.csproj", "{83C1B854-C3AD-4C1E-B2BC-799642039C3D}" @@ -145,10 +143,6 @@ Global {943CF0EB-80DF-4D2F-BF23-0EF6A5B2DA49}.Debug|Any CPU.Build.0 = Debug|Any CPU {943CF0EB-80DF-4D2F-BF23-0EF6A5B2DA49}.Release|Any CPU.ActiveCfg = Release|Any CPU {943CF0EB-80DF-4D2F-BF23-0EF6A5B2DA49}.Release|Any CPU.Build.0 = Release|Any CPU - {76F4AB6D-5249-4D0A-AAF8-D656E792AA34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {76F4AB6D-5249-4D0A-AAF8-D656E792AA34}.Debug|Any CPU.Build.0 = Debug|Any CPU - {76F4AB6D-5249-4D0A-AAF8-D656E792AA34}.Release|Any CPU.ActiveCfg = Release|Any CPU - {76F4AB6D-5249-4D0A-AAF8-D656E792AA34}.Release|Any CPU.Build.0 = Release|Any CPU {FA133D31-0DCC-4737-A76F-8AF6D8755EDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FA133D31-0DCC-4737-A76F-8AF6D8755EDE}.Debug|Any CPU.Build.0 = Debug|Any CPU {FA133D31-0DCC-4737-A76F-8AF6D8755EDE}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -257,7 +251,6 @@ Global {F297E0E8-3582-41BE-AB48-14D89413B7E0} = {195D2EF7-3938-4BF8-B483-170AD8FF5B84} {7348E3F2-3749-4D5F-AEA5-21C7D19FCC49} = {195D2EF7-3938-4BF8-B483-170AD8FF5B84} {943CF0EB-80DF-4D2F-BF23-0EF6A5B2DA49} = {195D2EF7-3938-4BF8-B483-170AD8FF5B84} - {76F4AB6D-5249-4D0A-AAF8-D656E792AA34} = {195D2EF7-3938-4BF8-B483-170AD8FF5B84} {FA133D31-0DCC-4737-A76F-8AF6D8755EDE} = {195D2EF7-3938-4BF8-B483-170AD8FF5B84} {83C1B854-C3AD-4C1E-B2BC-799642039C3D} = {195D2EF7-3938-4BF8-B483-170AD8FF5B84} {4BC736B4-4A2D-43C7-AD2D-7B24F5E35518} = {ACC32A2F-DF9C-4B91-80AE-F0585CDD59EC} diff --git a/Source/Lib/Common/Contexts/Displays/ContextBoundaryMeasurer.razor.cs b/Source/Lib/Common/Contexts/Displays/ContextBoundaryMeasurer.razor.cs index a140c8e90..d8bbfd77d 100644 --- a/Source/Lib/Common/Contexts/Displays/ContextBoundaryMeasurer.razor.cs +++ b/Source/Lib/Common/Contexts/Displays/ContextBoundaryMeasurer.razor.cs @@ -3,7 +3,6 @@ using Fluxor; using Microsoft.JSInterop; using System.Collections.Immutable; -using Luthetus.Common.RazorLib.JavaScriptObjects.Models; using Luthetus.Common.RazorLib.Contexts.States; using Luthetus.Common.RazorLib.Contexts.Models; using Luthetus.Common.RazorLib.Keys.Models; diff --git a/Source/Lib/Common/FileSystems/Models/AncestorDirectory.cs b/Source/Lib/Common/FileSystems/Models/AncestorDirectory.cs index cfe15c664..f17efea48 100644 --- a/Source/Lib/Common/FileSystems/Models/AncestorDirectory.cs +++ b/Source/Lib/Common/FileSystems/Models/AncestorDirectory.cs @@ -1,5 +1,13 @@ namespace Luthetus.Common.RazorLib.FileSystems.Models; +/// +/// This class exists alongside the type. +/// This is intentional, as if one treats an ancestor directory as an , +/// then far more "metadata" gets created.

+/// +/// This class allows for tracking of ancestor directories efficiently, +/// and if there is one of interest, then choose to create an from it. +///
public class AncestorDirectory { public AncestorDirectory( diff --git a/Source/Lib/Common/FileSystems/Models/IAbsolutePath.cs b/Source/Lib/Common/FileSystems/Models/IAbsolutePath.cs index bd8434b49..c76414393 100644 --- a/Source/Lib/Common/FileSystems/Models/IAbsolutePath.cs +++ b/Source/Lib/Common/FileSystems/Models/IAbsolutePath.cs @@ -4,4 +4,21 @@ public interface IAbsolutePath : IPath { public IFileSystemDrive? RootDrive { get; } public bool IsRootDirectory { get; } + + /// + /// One might prefer instead + /// of this method.

+ /// + /// This version adds an extra function invocation for no reason. To use + /// may be a negligible + /// optimization however.

+ /// + /// Keep this method here, it provides more clear documentation on how to create an instance + /// of . Having to invoke a method on the + /// is a bit hard to find. + ///
+ public static IAbsolutePath Factory(string path, bool isDirectory, IEnvironmentProvider environmentProvider) + { + return environmentProvider.AbsolutePathFactory(path, isDirectory); + } } diff --git a/Source/Lib/Common/FileSystems/Models/IRelativePath.cs b/Source/Lib/Common/FileSystems/Models/IRelativePath.cs index b8028d988..1c817d589 100644 --- a/Source/Lib/Common/FileSystems/Models/IRelativePath.cs +++ b/Source/Lib/Common/FileSystems/Models/IRelativePath.cs @@ -15,4 +15,21 @@ public interface IRelativePath : IPath /// Given "../../../Homework/math.txt". The count is 3. /// public int UpDirDirectiveCount { get; } + + /// + /// One might prefer instead + /// of this method.

+ /// + /// This version adds an extra function invocation for no reason. To use + /// may be a negligible + /// optimization however.

+ /// + /// Keep this method here, it provides more clear documentation on how to create an instance + /// of . Having to invoke a method on the + /// is a bit hard to find. + ///
+ public static IRelativePath Factory(string path, bool isDirectory, IEnvironmentProvider environmentProvider) + { + return environmentProvider.RelativePathFactory(path, isDirectory); + } } \ No newline at end of file diff --git a/Source/Lib/Common/JsRuntimes/Models/LuthetusCommonJavaScriptInteropApi.cs b/Source/Lib/Common/JsRuntimes/Models/LuthetusCommonJavaScriptInteropApi.cs index d21f95e9d..c9c65ba2d 100644 --- a/Source/Lib/Common/JsRuntimes/Models/LuthetusCommonJavaScriptInteropApi.cs +++ b/Source/Lib/Common/JsRuntimes/Models/LuthetusCommonJavaScriptInteropApi.cs @@ -41,9 +41,9 @@ public ValueTask LocalStorageSetItem(string key, object? value) value); } - public ValueTask LocalStorageGetItem(string key) + public ValueTask LocalStorageGetItem(string key) { - return _jsRuntime.InvokeAsync( + return _jsRuntime.InvokeAsync( "luthetusCommon.localStorageGetItem", key); } diff --git a/Source/Lib/Common/Luthetus.Common.RazorLib.csproj b/Source/Lib/Common/Luthetus.Common.RazorLib.csproj index 655a736f2..1629a25cf 100644 --- a/Source/Lib/Common/Luthetus.Common.RazorLib.csproj +++ b/Source/Lib/Common/Luthetus.Common.RazorLib.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -7,7 +7,7 @@ Luthetus Common A repository containing Blazor Components I've made which I commonly use in other projects. Luthetus.Common - 1.5.0 + 2.0.0 Hunter Freeman DotNet CSharp Blazor RazorComponents @@ -17,13 +17,17 @@ - - - - - - - + + + + + + + + + + + diff --git a/Source/Lib/Common/Panels/Displays/PanelGroupDisplay.razor b/Source/Lib/Common/Panels/Displays/PanelGroupDisplay.razor index 7c6d283f0..b3b5c7b84 100644 --- a/Source/Lib/Common/Panels/Displays/PanelGroupDisplay.razor +++ b/Source/Lib/Common/Panels/Displays/PanelGroupDisplay.razor @@ -4,9 +4,9 @@ @inherits Fluxor.Blazor.Web.Components.FluxorComponent @{ - var panelsState = PanelsStateWrap.Value; + var panelState = PanelStateWrap.Value; - var panelGroup = panelsState.PanelGroupList.FirstOrDefault(x => x.Key == PanelGroupKey); + var panelGroup = panelState.PanelGroupList.FirstOrDefault(x => x.Key == PanelGroupKey); IPanelTab? activePanelTab = null; @@ -48,7 +48,7 @@ } - @if (PanelsStateWrap.Value.DragEventArgs is not null) + @if (PanelStateWrap.Value.DragEventArgs is not null) {
PanelsStateWrap { get; set; } = null!; + private IState PanelStateWrap { get; set; } = null!; [Inject] private IDispatcher Dispatcher { get; set; } = null!; [Inject] @@ -84,9 +84,9 @@ private string GetPanelPositionCssClass() private async Task PassAlongSizeIfHiddenAsync() { - var panelsState = PanelsStateWrap.Value; + var panelState = PanelStateWrap.Value; - var panelGroup = panelsState.PanelGroupList.FirstOrDefault(x => x.Key == PanelGroupKey); + var panelGroup = panelState.PanelGroupList.FirstOrDefault(x => x.Key == PanelGroupKey); if (panelGroup is not null) { @@ -141,27 +141,27 @@ private string GetElementDimensionsStyleString(PanelGroup? panelGroup, IPanelTab private Task TopDropzoneOnMouseUp(MouseEventArgs mouseEventArgs) { - var panelsState = PanelsStateWrap.Value; + var panelState = PanelStateWrap.Value; - var panelGroup = panelsState.PanelGroupList.FirstOrDefault(x => x.Key == PanelGroupKey); + var panelGroup = panelState.PanelGroupList.FirstOrDefault(x => x.Key == PanelGroupKey); if (panelGroup is null) return Task.CompletedTask; - var panelDragEventArgs = panelsState.DragEventArgs; + var panelDragEventArgs = panelState.DragEventArgs; if (panelDragEventArgs is not null) { - Dispatcher.Dispatch(new PanelsState.DisposePanelTabAction( + Dispatcher.Dispatch(new PanelState.DisposePanelTabAction( panelDragEventArgs.Value.PanelGroup.Key, panelDragEventArgs.Value.PanelTab.Key)); - Dispatcher.Dispatch(new PanelsState.RegisterPanelTabAction( + Dispatcher.Dispatch(new PanelState.RegisterPanelTabAction( panelGroup.Key, panelDragEventArgs.Value.PanelTab, true)); - Dispatcher.Dispatch(new PanelsState.SetDragEventArgsAction(null)); + Dispatcher.Dispatch(new PanelState.SetDragEventArgsAction(null)); Dispatcher.Dispatch(new DragState.WithAction(inState => inState with { @@ -175,27 +175,27 @@ private Task TopDropzoneOnMouseUp(MouseEventArgs mouseEventArgs) private Task BottomDropzoneOnMouseUp(MouseEventArgs mouseEventArgs) { - var panelsState = PanelsStateWrap.Value; + var panelState = PanelStateWrap.Value; - var panelGroup = panelsState.PanelGroupList.FirstOrDefault(x => x.Key == PanelGroupKey); + var panelGroup = panelState.PanelGroupList.FirstOrDefault(x => x.Key == PanelGroupKey); if (panelGroup is null) return Task.CompletedTask; - var panelDragEventArgs = panelsState.DragEventArgs; + var panelDragEventArgs = panelState.DragEventArgs; if (panelDragEventArgs is not null) { - Dispatcher.Dispatch(new PanelsState.DisposePanelTabAction( + Dispatcher.Dispatch(new PanelState.DisposePanelTabAction( panelDragEventArgs.Value.PanelGroup.Key, panelDragEventArgs.Value.PanelTab.Key)); - Dispatcher.Dispatch(new PanelsState.RegisterPanelTabAction( + Dispatcher.Dispatch(new PanelState.RegisterPanelTabAction( panelGroup.Key, panelDragEventArgs.Value.PanelTab, false)); - Dispatcher.Dispatch(new PanelsState.SetDragEventArgsAction(null)); + Dispatcher.Dispatch(new PanelState.SetDragEventArgsAction(null)); Dispatcher.Dispatch(new DragState.WithAction(inState => inState with { diff --git a/Source/Lib/Common/Panels/Models/Panel.cs b/Source/Lib/Common/Panels/Models/Panel.cs index 48cac6d48..464b9031c 100644 --- a/Source/Lib/Common/Panels/Models/Panel.cs +++ b/Source/Lib/Common/Panels/Models/Panel.cs @@ -199,7 +199,7 @@ public Task OnDragEndAsync(MouseEventArgs mouseEventArgs, IDropzone? dropzone) { if (panelGroup is not null) { - Dispatcher.Dispatch(new PanelsState.DisposePanelTabAction( + Dispatcher.Dispatch(new PanelState.DisposePanelTabAction( panelGroup.Key, Key)); } @@ -223,7 +223,7 @@ public Task OnDragEndAsync(MouseEventArgs mouseEventArgs, IDropzone? dropzone) { if (panelGroup is not null) { - Dispatcher.Dispatch(new PanelsState.DisposePanelTabAction( + Dispatcher.Dispatch(new PanelState.DisposePanelTabAction( panelGroup.Key, Key)); } @@ -242,7 +242,7 @@ public Task OnDragEndAsync(MouseEventArgs mouseEventArgs, IDropzone? dropzone) ? true : false; - Dispatcher.Dispatch(new PanelsState.RegisterPanelTabAction( + Dispatcher.Dispatch(new PanelState.RegisterPanelTabAction( panelGroupDropzone.PanelGroupKey, this, insertAtIndexZero)); diff --git a/Source/Lib/Common/Panels/Models/PanelFacts.cs b/Source/Lib/Common/Panels/Models/PanelFacts.cs index d51a398cc..ccac55bbc 100644 --- a/Source/Lib/Common/Panels/Models/PanelFacts.cs +++ b/Source/Lib/Common/Panels/Models/PanelFacts.cs @@ -9,18 +9,18 @@ public static class PanelFacts public static readonly Key RightPanelGroupKey = Key.NewKey(); public static readonly Key BottomPanelGroupKey = Key.NewKey(); - public static PanelGroup GetTopLeftPanelGroup(PanelsState panelsState) + public static PanelGroup GetTopLeftPanelGroup(PanelState panelState) { - return panelsState.PanelGroupList.First(x => x.Key == LeftPanelGroupKey); + return panelState.PanelGroupList.First(x => x.Key == LeftPanelGroupKey); } - public static PanelGroup GetTopRightPanelGroup(PanelsState panelsState) + public static PanelGroup GetTopRightPanelGroup(PanelState panelState) { - return panelsState.PanelGroupList.First(x => x.Key == RightPanelGroupKey); + return panelState.PanelGroupList.First(x => x.Key == RightPanelGroupKey); } - public static PanelGroup GetBottomPanelGroup(PanelsState panelsState) + public static PanelGroup GetBottomPanelGroup(PanelState panelState) { - return panelsState.PanelGroupList.First(x => x.Key == BottomPanelGroupKey); + return panelState.PanelGroupList.First(x => x.Key == BottomPanelGroupKey); } } \ No newline at end of file diff --git a/Source/Lib/Common/Panels/Models/PanelGroup.cs b/Source/Lib/Common/Panels/Models/PanelGroup.cs index ae6e68d65..86b2d11ca 100644 --- a/Source/Lib/Common/Panels/Models/PanelGroup.cs +++ b/Source/Lib/Common/Panels/Models/PanelGroup.cs @@ -35,9 +35,9 @@ public Task OnClickAsync(ITab tab, MouseEventArgs mouseEventArgs) return Task.CompletedTask; if (GetIsActive(tab)) - Dispatcher.Dispatch(new PanelsState.SetActivePanelTabAction(Key, Key.Empty)); + Dispatcher.Dispatch(new PanelState.SetActivePanelTabAction(Key, Key.Empty)); else - Dispatcher.Dispatch(new PanelsState.SetActivePanelTabAction(Key, panelTab.Key)); + Dispatcher.Dispatch(new PanelState.SetActivePanelTabAction(Key, panelTab.Key)); return Task.CompletedTask; } @@ -52,7 +52,7 @@ public Task CloseAsync(ITab tab) if (tab is not IPanelTab panelTab) return Task.CompletedTask; - Dispatcher.Dispatch(new PanelsState.DisposePanelTabAction(Key, panelTab.Key)); + Dispatcher.Dispatch(new PanelState.DisposePanelTabAction(Key, panelTab.Key)); return Task.CompletedTask; } diff --git a/Source/Lib/Common/Panels/States/PanelsState.Actions.cs b/Source/Lib/Common/Panels/States/PanelsState.Actions.cs index 2700978d0..66932637a 100644 --- a/Source/Lib/Common/Panels/States/PanelsState.Actions.cs +++ b/Source/Lib/Common/Panels/States/PanelsState.Actions.cs @@ -4,7 +4,7 @@ namespace Luthetus.Common.RazorLib.Panels.States; -public partial record PanelsState +public partial record PanelState { public record RegisterPanelAction(Panel Panel); public record DisposePanelAction(Key PanelKey); diff --git a/Source/Lib/Common/Panels/States/PanelsState.Main.cs b/Source/Lib/Common/Panels/States/PanelsState.Main.cs index eb694b969..a246380c3 100644 --- a/Source/Lib/Common/Panels/States/PanelsState.Main.cs +++ b/Source/Lib/Common/Panels/States/PanelsState.Main.cs @@ -11,11 +11,11 @@ namespace Luthetus.Common.RazorLib.Panels.States; /// TODO: SphagettiCode - The resizing and hiding/showing is a bit scuffed. (2023-09-19) /// [FeatureState] -public partial record PanelsState( +public partial record PanelState( ImmutableArray PanelGroupList, ImmutableArray PanelList) { - public PanelsState() : this(ImmutableArray.Empty, ImmutableArray.Empty) + public PanelState() : this(ImmutableArray.Empty, ImmutableArray.Empty) { var topLeftGroup = ConstructTopLeftGroup(); var topRightGroup = ConstructTopRightGroup(); diff --git a/Source/Lib/Common/Panels/States/PanelsState.Reducer.cs b/Source/Lib/Common/Panels/States/PanelsState.Reducer.cs index 4c1b4b2a6..8d89d3adc 100644 --- a/Source/Lib/Common/Panels/States/PanelsState.Reducer.cs +++ b/Source/Lib/Common/Panels/States/PanelsState.Reducer.cs @@ -2,13 +2,13 @@ namespace Luthetus.Common.RazorLib.Panels.States; -public partial record PanelsState +public partial record PanelState { public class Reducer { [ReducerMethod] - public static PanelsState ReduceRegisterPanelAction( - PanelsState inState, + public static PanelState ReduceRegisterPanelAction( + PanelState inState, RegisterPanelAction registerPanelAction) { if (inState.PanelList.Any(x => x.Key == registerPanelAction.Panel.Key)) @@ -20,8 +20,8 @@ public static PanelsState ReduceRegisterPanelAction( } [ReducerMethod] - public static PanelsState ReduceDisposePanelAction( - PanelsState inState, + public static PanelState ReduceDisposePanelAction( + PanelState inState, DisposePanelAction disposePanelAction) { var inPanel = inState.PanelList.FirstOrDefault( @@ -36,8 +36,8 @@ public static PanelsState ReduceDisposePanelAction( } [ReducerMethod] - public static PanelsState ReduceRegisterPanelGroupAction( - PanelsState inState, + public static PanelState ReduceRegisterPanelGroupAction( + PanelState inState, RegisterPanelGroupAction registerPanelGroupAction) { if (inState.PanelGroupList.Any(x => x.Key == registerPanelGroupAction.PanelGroup.Key)) @@ -49,8 +49,8 @@ public static PanelsState ReduceRegisterPanelGroupAction( } [ReducerMethod] - public static PanelsState ReduceDisposePanelGroupAction( - PanelsState inState, + public static PanelState ReduceDisposePanelGroupAction( + PanelState inState, DisposePanelGroupAction disposePanelGroupAction) { var inPanelGroup = inState.PanelGroupList.FirstOrDefault( @@ -65,8 +65,8 @@ public static PanelsState ReduceDisposePanelGroupAction( } [ReducerMethod] - public static PanelsState ReduceRegisterPanelTabAction( - PanelsState inState, + public static PanelState ReduceRegisterPanelTabAction( + PanelState inState, RegisterPanelTabAction registerPanelTabAction) { var inPanelGroup = inState.PanelGroupList.FirstOrDefault( @@ -95,8 +95,8 @@ public static PanelsState ReduceRegisterPanelTabAction( } [ReducerMethod] - public static PanelsState ReduceDisposePanelTabAction( - PanelsState inState, + public static PanelState ReduceDisposePanelTabAction( + PanelState inState, DisposePanelTabAction disposePanelTabAction) { var inPanelGroup = inState.PanelGroupList.FirstOrDefault( @@ -122,8 +122,8 @@ public static PanelsState ReduceDisposePanelTabAction( } [ReducerMethod] - public static PanelsState ReduceSetActivePanelTabAction( - PanelsState inState, + public static PanelState ReduceSetActivePanelTabAction( + PanelState inState, SetActivePanelTabAction setActivePanelTabAction) { var inPanelGroup = inState.PanelGroupList.FirstOrDefault( @@ -141,8 +141,8 @@ public static PanelsState ReduceSetActivePanelTabAction( } [ReducerMethod] - public static PanelsState ReduceSetPanelTabAsActiveByContextRecordKeyAction( - PanelsState inState, + public static PanelState ReduceSetPanelTabAsActiveByContextRecordKeyAction( + PanelState inState, SetPanelTabAsActiveByContextRecordKeyAction setPanelTabAsActiveByContextRecordKeyAction) { var inPanelGroup = inState.PanelGroupList.FirstOrDefault(x => x.TabList @@ -163,8 +163,8 @@ public static PanelsState ReduceSetPanelTabAsActiveByContextRecordKeyAction( } [ReducerMethod] - public static PanelsState ReduceSetDragEventArgsAction( - PanelsState inState, + public static PanelState ReduceSetDragEventArgsAction( + PanelState inState, SetDragEventArgsAction setDragEventArgsAction) { return inState with diff --git a/Source/Lib/Common/Tabs/Displays/TabDisplay.razor.cs b/Source/Lib/Common/Tabs/Displays/TabDisplay.razor.cs index 91f84118c..f422cc56e 100644 --- a/Source/Lib/Common/Tabs/Displays/TabDisplay.razor.cs +++ b/Source/Lib/Common/Tabs/Displays/TabDisplay.razor.cs @@ -2,7 +2,6 @@ using Luthetus.Common.RazorLib.Dimensions.Models; using Luthetus.Common.RazorLib.Drags.Displays; using Luthetus.Common.RazorLib.Resizes.Models; -using Luthetus.Common.RazorLib.JavaScriptObjects.Models; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using Microsoft.JSInterop; diff --git a/Source/Lib/Common/wwwroot/luthetusCommon.js b/Source/Lib/Common/wwwroot/luthetusCommon.js index 99e834264..e2609b9d9 100644 --- a/Source/Lib/Common/wwwroot/luthetusCommon.js +++ b/Source/Lib/Common/wwwroot/luthetusCommon.js @@ -78,7 +78,25 @@ } }); } catch (e) { - return ""; + // Debugging Linux-Ubuntu (2024-04-28) + // ----------------------------------- + // Reading clipboard is not working. + // + // Fixed with the following inner-try/catch block. + // + // This fix upsets me. Seemingly the permission + // "clipboard-read" doesn't exist for some user-agents + // But so long as you don't check for permission it lets you read + // the clipboard? + try { + return navigator.clipboard + .readText() + .then((clipText) => { + return clipText; + }); + } catch (innerException) { + return ""; + } } }, setClipboard: function (value) { diff --git a/Source/Lib/CompilerServices/C/Luthetus.CompilerServices.Lang.C.csproj b/Source/Lib/CompilerServices/C/Luthetus.CompilerServices.Lang.C.csproj index fd22b41fb..2a32439dd 100644 --- a/Source/Lib/CompilerServices/C/Luthetus.CompilerServices.Lang.C.csproj +++ b/Source/Lib/CompilerServices/C/Luthetus.CompilerServices.Lang.C.csproj @@ -7,7 +7,7 @@ Luthetus Compiler Service C A C implementation of the ICompilerService interface, which comes from the "Luthetus.TextEditor" repository. Luthetus.CompilerServices.C - 1.5.0 + 2.0.0 Hunter Freeman DotNet CSharp Blazor RazorComponents diff --git a/Source/Lib/CompilerServices/CSharp/Luthetus.CompilerServices.Lang.CSharp.csproj b/Source/Lib/CompilerServices/CSharp/Luthetus.CompilerServices.Lang.CSharp.csproj index fe6e9154f..1a45b9d13 100644 --- a/Source/Lib/CompilerServices/CSharp/Luthetus.CompilerServices.Lang.CSharp.csproj +++ b/Source/Lib/CompilerServices/CSharp/Luthetus.CompilerServices.Lang.CSharp.csproj @@ -7,7 +7,7 @@ Luthetus Compiler Service C# A C# implementation of the ICompilerService interface, which comes from the "Luthetus.TextEditor" repository. Luthetus.CompilerServices.CSharp - 1.5.0 + 2.0.0 Hunter Freeman DotNet CSharp Blazor RazorComponents diff --git a/Source/Lib/CompilerServices/CSharpProject/Luthetus.CompilerServices.Lang.CSharpProject.csproj b/Source/Lib/CompilerServices/CSharpProject/Luthetus.CompilerServices.Lang.CSharpProject.csproj index 7cf8dfd83..14685d250 100644 --- a/Source/Lib/CompilerServices/CSharpProject/Luthetus.CompilerServices.Lang.CSharpProject.csproj +++ b/Source/Lib/CompilerServices/CSharpProject/Luthetus.CompilerServices.Lang.CSharpProject.csproj @@ -7,7 +7,7 @@ Luthetus Compiler Service C# Project A C# Project implementation of the ICompilerService interface, which comes from the "Luthetus.TextEditor" repository. Luthetus.CompilerServices.CSharpProject - 1.5.0 + 2.0.0 Hunter Freeman DotNet CSharp Blazor RazorComponents diff --git a/Source/Lib/CompilerServices/Css/Css/SyntaxActors/CssSyntaxTree.cs b/Source/Lib/CompilerServices/Css/Css/SyntaxActors/CssSyntaxTree.cs index c1b4df1e1..9d7275000 100644 --- a/Source/Lib/CompilerServices/Css/Css/SyntaxActors/CssSyntaxTree.cs +++ b/Source/Lib/CompilerServices/Css/Css/SyntaxActors/CssSyntaxTree.cs @@ -7,7 +7,6 @@ using Luthetus.TextEditor.RazorLib.CompilerServices; using Luthetus.TextEditor.RazorLib.CompilerServices.Utility; using Luthetus.TextEditor.RazorLib.CompilerServices.Facts; -using Luthetus.Common.RazorLib.Exceptions; using Luthetus.TextEditor.RazorLib.Exceptions; namespace Luthetus.CompilerServices.Lang.Css.Css.SyntaxActors; diff --git a/Source/Lib/CompilerServices/Css/Luthetus.CompilerServices.Lang.Css.csproj b/Source/Lib/CompilerServices/Css/Luthetus.CompilerServices.Lang.Css.csproj index d36e7fc5d..d888a32b9 100644 --- a/Source/Lib/CompilerServices/Css/Luthetus.CompilerServices.Lang.Css.csproj +++ b/Source/Lib/CompilerServices/Css/Luthetus.CompilerServices.Lang.Css.csproj @@ -7,7 +7,7 @@ Luthetus Compiler Service CSS A CSS implementation of the ICompilerService interface, which comes from the "Luthetus.TextEditor" repository. Luthetus.CompilerServices.Css - 1.5.0 + 2.0.0 Hunter Freeman DotNet CSharp Blazor RazorComponents diff --git a/Source/Lib/CompilerServices/DotNetSolution/Luthetus.CompilerServices.Lang.DotNetSolution.csproj b/Source/Lib/CompilerServices/DotNetSolution/Luthetus.CompilerServices.Lang.DotNetSolution.csproj index 4129ad3a8..3c86034d1 100644 --- a/Source/Lib/CompilerServices/DotNetSolution/Luthetus.CompilerServices.Lang.DotNetSolution.csproj +++ b/Source/Lib/CompilerServices/DotNetSolution/Luthetus.CompilerServices.Lang.DotNetSolution.csproj @@ -7,7 +7,7 @@ Luthetus Compiler Service .NET Solution A .NET Solution implementation of the ICompilerService interface, which comes from the "Luthetus.TextEditor" repository. Luthetus.CompilerServices.DotNetSolution - 1.5.0 + 2.0.0 Hunter Freeman DotNet CSharp Blazor RazorComponents diff --git a/Source/Lib/CompilerServices/FSharp/Luthetus.CompilerServices.Lang.FSharp.csproj b/Source/Lib/CompilerServices/FSharp/Luthetus.CompilerServices.Lang.FSharp.csproj index 9f71719f4..7973c8466 100644 --- a/Source/Lib/CompilerServices/FSharp/Luthetus.CompilerServices.Lang.FSharp.csproj +++ b/Source/Lib/CompilerServices/FSharp/Luthetus.CompilerServices.Lang.FSharp.csproj @@ -7,7 +7,7 @@ Luthetus Compiler Service F# A F# implementation of the ICompilerService interface, which comes from the "Luthetus.TextEditor" repository. Luthetus.CompilerServices.FSharp - 1.5.0 + 2.0.0 Hunter Freeman DotNet CSharp Blazor RazorComponents diff --git a/Source/Lib/CompilerServices/JavaScript/Luthetus.CompilerServices.Lang.JavaScript.csproj b/Source/Lib/CompilerServices/JavaScript/Luthetus.CompilerServices.Lang.JavaScript.csproj index a682abdc3..7f2d3d5b3 100644 --- a/Source/Lib/CompilerServices/JavaScript/Luthetus.CompilerServices.Lang.JavaScript.csproj +++ b/Source/Lib/CompilerServices/JavaScript/Luthetus.CompilerServices.Lang.JavaScript.csproj @@ -7,7 +7,7 @@ Luthetus Compiler Service JavaScript A JavaScript implementation of the ICompilerService interface, which comes from the "Luthetus.TextEditor" repository. Luthetus.CompilerServices.JavaScript - 1.5.0 + 2.0.0 Hunter Freeman DotNet CSharp Blazor RazorComponents diff --git a/Source/Lib/CompilerServices/Json/Luthetus.CompilerServices.Lang.Json.csproj b/Source/Lib/CompilerServices/Json/Luthetus.CompilerServices.Lang.Json.csproj index 12cfe54e4..71508f0c0 100644 --- a/Source/Lib/CompilerServices/Json/Luthetus.CompilerServices.Lang.Json.csproj +++ b/Source/Lib/CompilerServices/Json/Luthetus.CompilerServices.Lang.Json.csproj @@ -7,7 +7,7 @@ Luthetus Compiler Service JSON A JSON implementation of the ICompilerService interface, which comes from the "Luthetus.TextEditor" repository. Luthetus.CompilerServices.Json - 1.5.0 + 2.0.0 Hunter Freeman DotNet CSharp Blazor RazorComponents diff --git a/Source/Lib/CompilerServices/Razor/Luthetus.CompilerServices.Lang.Razor.csproj b/Source/Lib/CompilerServices/Razor/Luthetus.CompilerServices.Lang.Razor.csproj index 034937d36..b25ab2565 100644 --- a/Source/Lib/CompilerServices/Razor/Luthetus.CompilerServices.Lang.Razor.csproj +++ b/Source/Lib/CompilerServices/Razor/Luthetus.CompilerServices.Lang.Razor.csproj @@ -7,7 +7,7 @@ Luthetus Compiler Service Razor A Razor implementation of the ICompilerService interface, which comes from the "Luthetus.TextEditor" repository. Luthetus.CompilerServices.Razor - 1.5.0 + 2.0.0 Hunter Freeman DotNet CSharp Blazor RazorComponents diff --git a/Source/Lib/CompilerServices/TypeScript/Luthetus.CompilerServices.Lang.TypeScript.csproj b/Source/Lib/CompilerServices/TypeScript/Luthetus.CompilerServices.Lang.TypeScript.csproj index 41bde1ed9..0a128ea13 100644 --- a/Source/Lib/CompilerServices/TypeScript/Luthetus.CompilerServices.Lang.TypeScript.csproj +++ b/Source/Lib/CompilerServices/TypeScript/Luthetus.CompilerServices.Lang.TypeScript.csproj @@ -7,7 +7,7 @@ Luthetus Compiler Service TypeScript A TypeScript implementation of the ICompilerService interface, which comes from the "Luthetus.TextEditor" repository. Luthetus.CompilerServices.TypeScript - 1.5.0 + 2.0.0 Hunter Freeman DotNet CSharp Blazor RazorComponents diff --git a/Source/Lib/CompilerServices/Xml/Luthetus.CompilerServices.Lang.Xml.csproj b/Source/Lib/CompilerServices/Xml/Luthetus.CompilerServices.Lang.Xml.csproj index 7c02119f0..23b5ba795 100644 --- a/Source/Lib/CompilerServices/Xml/Luthetus.CompilerServices.Lang.Xml.csproj +++ b/Source/Lib/CompilerServices/Xml/Luthetus.CompilerServices.Lang.Xml.csproj @@ -7,7 +7,7 @@ Luthetus Compiler Service HTML/XML An HTML/XML implementation of the ICompilerService interface, which comes from the "Luthetus.TextEditor" repository. Luthetus.CompilerServices.Xml - 1.5.0 + 2.0.0 Hunter Freeman DotNet CSharp Blazor RazorComponents diff --git a/Source/Lib/Ide/Host.Photino/Program.cs b/Source/Lib/Ide/Host.Photino/Program.cs index 11df45550..62ee33196 100644 --- a/Source/Lib/Ide/Host.Photino/Program.cs +++ b/Source/Lib/Ide/Host.Photino/Program.cs @@ -46,10 +46,14 @@ static void Main(string[] args) .SetLeft(50) .SetTop(50); - if (Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) == "C:\\Users\\hunte") + // Personal settings to have closing and reopening the IDE be exactly where I want while developing. { - app.MainWindow.SetLeft(1_355); - } + var specialFolderUserProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + if (specialFolderUserProfile == "C:\\Users\\hunte") + app.MainWindow.SetLeft(1_355); + else if (specialFolderUserProfile == "/home/hunter") + app.MainWindow.SetLeft(1_100).SetTop(100).SetHeight(1900); + } var continuousStartCts = new CancellationTokenSource(); var blockingStartCts = new CancellationTokenSource(); diff --git a/Source/Lib/Ide/Ide.RazorLib/CSharpProjectForms/Displays/CSharpProjectFormDisplay.razor.cs b/Source/Lib/Ide/Ide.RazorLib/CSharpProjectForms/Displays/CSharpProjectFormDisplay.razor.cs index 70aff3155..e4f6391d5 100644 --- a/Source/Lib/Ide/Ide.RazorLib/CSharpProjectForms/Displays/CSharpProjectFormDisplay.razor.cs +++ b/Source/Lib/Ide/Ide.RazorLib/CSharpProjectForms/Displays/CSharpProjectFormDisplay.razor.cs @@ -133,8 +133,11 @@ private async Task EnqueueDotNetNewListAsync() _viewModel.NewCSharpProjectCancellationTokenSource.Token, async () => { - var output = generalTerminal.ReadStandardOut(_viewModel.LoadProjectTemplatesTerminalCommandKey); - + var success = generalTerminal.TryGetTerminalCommandTextSpan( + _viewModel.LoadProjectTemplatesTerminalCommandKey, + out var terminalCommandTextSpan); + + var output = terminalCommandTextSpan?.GetText(); if (output is not null) { _viewModel.ProjectTemplateList = DotNetCliOutputParser.ParseDotNetNewListTerminalOutput(output); @@ -175,8 +178,11 @@ private async Task EnqueueDotnetNewListDeprecatedAsync() _viewModel.NewCSharpProjectCancellationTokenSource.Token, async () => { - var output = generalTerminal.ReadStandardOut(_viewModel.LoadProjectTemplatesTerminalCommandKey); + var success = generalTerminal.TryGetTerminalCommandTextSpan( + _viewModel.LoadProjectTemplatesTerminalCommandKey, + out var terminalCommandTextSpan); + var output = terminalCommandTextSpan?.GetText(); if (output is not null) { _viewModel.ProjectTemplateList = DotNetCliOutputParser.ParseDotNetNewListTerminalOutput(output); diff --git a/Source/Lib/Ide/Ide.RazorLib/CodeSearches/States/CodeSearchState.Effector.cs b/Source/Lib/Ide/Ide.RazorLib/CodeSearches/States/CodeSearchState.Effector.cs index 46f2ccae1..1c19573c0 100644 --- a/Source/Lib/Ide/Ide.RazorLib/CodeSearches/States/CodeSearchState.Effector.cs +++ b/Source/Lib/Ide/Ide.RazorLib/CodeSearches/States/CodeSearchState.Effector.cs @@ -24,6 +24,16 @@ public Effector( _fileSystemProvider = fileSystemProvider; } + /// + /// TODO: This method makes use of 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) + /// + /// + /// + /// [EffectMethod] public Task HandleSearchEffect( SearchEffect searchEffect, diff --git a/Source/Lib/Ide/Ide.RazorLib/Commands/CommandFactory.cs b/Source/Lib/Ide/Ide.RazorLib/Commands/CommandFactory.cs index 2d89226df..a5498711d 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Commands/CommandFactory.cs +++ b/Source/Lib/Ide/Ide.RazorLib/Commands/CommandFactory.cs @@ -17,14 +17,13 @@ using Luthetus.Ide.RazorLib.CodeSearches.Displays; using Luthetus.Common.RazorLib.Dynamics.Models; using Luthetus.TextEditor.RazorLib; -using Luthetus.TextEditor.RazorLib.JsRuntimes; using Luthetus.Common.RazorLib.JsRuntimes.Models; namespace Luthetus.Ide.RazorLib.Commands; public class CommandFactory : ICommandFactory { - private readonly IState _panelsStateWrap; + private readonly IState _panelStateWrap; private readonly ITextEditorService _textEditorService; private readonly ITreeViewService _treeViewService; private readonly IEnvironmentProvider _environmentProvider; @@ -35,14 +34,14 @@ public CommandFactory( ITextEditorService textEditorService, ITreeViewService treeViewService, IEnvironmentProvider environmentProvider, - IState panelsStateWrap, + IState panelStateWrap, IDispatcher dispatcher, IJSRuntime jsRuntime) { _textEditorService = textEditorService; _treeViewService = treeViewService; _environmentProvider = environmentProvider; - _panelsStateWrap = panelsStateWrap; + _panelStateWrap = panelStateWrap; _dispatcher = dispatcher; _jsRuntime = jsRuntime; } @@ -324,7 +323,7 @@ public CommandNoType ConstructFocusContextElementCommand( if (!success) { - _dispatcher.Dispatch(new PanelsState.SetPanelTabAsActiveByContextRecordKeyAction( + _dispatcher.Dispatch(new PanelState.SetPanelTabAsActiveByContextRecordKeyAction( contextRecord.ContextKey)); _ = await TrySetFocus(); diff --git a/Source/Lib/Ide/Ide.RazorLib/DotNetSolutions/States/DotNetSolutionSync.Tasks.cs b/Source/Lib/Ide/Ide.RazorLib/DotNetSolutions/States/DotNetSolutionSync.Tasks.cs index 06d048375..68e55f219 100644 --- a/Source/Lib/Ide/Ide.RazorLib/DotNetSolutions/States/DotNetSolutionSync.Tasks.cs +++ b/Source/Lib/Ide/Ide.RazorLib/DotNetSolutions/States/DotNetSolutionSync.Tasks.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using System.Runtime.InteropServices; using Luthetus.Common.RazorLib.TreeViews.Models; using Luthetus.Common.RazorLib.Namespaces.Models; using Luthetus.Common.RazorLib.FileSystems.Models; @@ -113,6 +114,20 @@ private async Task SetDotNetSolutionAsync(IAbsolutePath inSolutionAbsolutePath) foreach (var project in parser.DotNetProjectList) { var relativePathFromSolutionFileString = project.RelativePathFromSolutionFileString; + + // Debugging Linux-Ubuntu (2024-04-28) + // ----------------------------------- + // It is believed, that Linux-Ubuntu is not fully working correctly, + // due to the directory separator character at the os level being '/', + // meanwhile the .NET solution has as its directory separator character '\'. + // + // Will perform a string.Replace("\\", "/") here. And if it solves the issue, + // then some standard way of doing this needs to be made available in the IEnvironmentProvider. + // + // Okay, this single replacement fixes 99% of the solution explorer issue. + // And I say 99% instead of 100% just because I haven't tested every single part of it yet. + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + relativePathFromSolutionFileString = relativePathFromSolutionFileString.Replace("\\", "/"); // Solution Folders do not exist on the filesystem. Therefore their absolute path is not guaranteed to be unique // One can use the ProjectIdGuid however, when working with a SolutionFolder to make the absolute path unique. @@ -180,7 +195,7 @@ private async Task SetDotNetSolutionAsync(IAbsolutePath inSolutionAbsolutePath) var changeDirectoryCommand = new TerminalCommand( Key.NewKey(), - new FormattedCommand(string.Empty, new string[] { }), + new FormattedCommand("cd", new string[] { }), parentDirectory.Value, CancellationToken.None); @@ -193,7 +208,7 @@ private async Task SetDotNetSolutionAsync(IAbsolutePath inSolutionAbsolutePath) var changeDirectoryCommand = new TerminalCommand( Key.NewKey(), - new FormattedCommand(string.Empty, new string[] { }), + new FormattedCommand("cd", new string[] { }), parentDirectory.Value, CancellationToken.None); diff --git a/Source/Lib/Ide/Ide.RazorLib/Events/Models/OnOutput.cs b/Source/Lib/Ide/Ide.RazorLib/Events/Models/OnOutput.cs new file mode 100644 index 000000000..faef7861d --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Events/Models/OnOutput.cs @@ -0,0 +1,144 @@ +using Luthetus.Common.RazorLib.BackgroundTasks.Models; +using Luthetus.Common.RazorLib.Keys.Models; +using Luthetus.Ide.RazorLib.Terminals.Models; +using Luthetus.TextEditor.RazorLib; +using Luthetus.TextEditor.RazorLib.CompilerServices.Syntax.Symbols; +using Luthetus.TextEditor.RazorLib.Lexes.Models; +using Luthetus.TextEditor.RazorLib.TextEditors.Displays; +using Luthetus.TextEditor.RazorLib.TextEditors.Models; + +namespace Luthetus.Ide.RazorLib.Events.Models; + +/// +/// This class is what one would use. Then, if batching is applicable, +/// an instance of will be made in place +/// of the events which are being batched.

+/// +/// This class is for use with command line text output. +/// For example, every time a line of output is written to stdout +/// one could furthermore invoke this event to write to the text editor +/// in a batched manner. +///
+public class OnOutput : ITextEditorTask +{ + private readonly TerminalCommandBoundary _terminalCommandBoundary; + + public OnOutput( + int outputOffset, + string output, + List outputTextSpanList, + ResourceUri resourceUri, + ITextEditorService textEditorService, + TerminalCommandBoundary terminalCommandBoundary, + Key viewModelKey) + { + OutputOffset = outputOffset; + Output = output; + _outputTextSpanList = outputTextSpanList; + ResourceUri = resourceUri; + TextEditorService = textEditorService; + _terminalCommandBoundary = terminalCommandBoundary; + ViewModelKey = viewModelKey; + } + + private List _outputTextSpanList; + + public Key BackgroundTaskKey { get; } = Key.NewKey(); + public Key QueueKey { get; } = ContinuousBackgroundTaskWorker.GetQueueKey(); + public string Name { get; } = nameof(OnOutput); + public Task? WorkProgress { get; } + public int OutputOffset { get; } + public string Output { get; } + public ResourceUri ResourceUri { get; } + public ITextEditorService TextEditorService { get; } + public Key ViewModelKey { get; } + + public TimeSpan ThrottleTimeSpan => TextEditorViewModelDisplay.TextEditorEvents.ThrottleDelayDefault; + + public async Task InvokeWithEditContext(IEditContext editContext) + { + var modelModifier = editContext.GetModelModifier(ResourceUri); + var viewModelModifier = editContext.GetViewModelModifier(ViewModelKey); + var cursorModifierBag = editContext.GetCursorModifierBag(viewModelModifier?.ViewModel); + var primaryCursorModifier = editContext.GetPrimaryCursorModifier(cursorModifierBag); + + if (modelModifier is null || viewModelModifier is null || cursorModifierBag is null || primaryCursorModifier is null) + return; + + var entryPositionIndex = modelModifier.GetPositionIndex(primaryCursorModifier); + _terminalCommandBoundary.StartPositionIndexInclusive ??= entryPositionIndex; + + await TextEditorService.ModelApi.InsertTextFactory( + ResourceUri, + ViewModelKey, + Output, + CancellationToken.None) + .Invoke(editContext) + .ConfigureAwait(false); + + var terminalCompilerService = (TerminalCompilerService)modelModifier.CompilerService; + if (terminalCompilerService.GetCompilerServiceResourceFor(modelModifier.ResourceUri) is not TerminalResource terminalResource) + return; + + _outputTextSpanList = _outputTextSpanList.Select(x => x with + { + StartingIndexInclusive = entryPositionIndex + x.StartingIndexInclusive, + EndingIndexExclusive = entryPositionIndex + x.EndingIndexExclusive, + ResourceUri = ResourceUri, + SourceText = modelModifier.GetAllText(), + }).ToList(); + + terminalResource.ManualDecorationTextSpanList.AddRange(_outputTextSpanList); + terminalResource.ManualSymbolList.AddRange(_outputTextSpanList.Select(x => new SourceFileSymbol(x))); + + await editContext.TextEditorService.ModelApi.ApplyDecorationRangeFactory( + modelModifier.ResourceUri, + terminalResource.GetTokenTextSpans()) + .Invoke(editContext) + .ConfigureAwait(false); + + _terminalCommandBoundary.EndPositionIndexExclusive = modelModifier.GetPositionIndex(primaryCursorModifier); + } + + public IBackgroundTask? BatchOrDefault(IBackgroundTask oldEvent) + { + if (oldEvent is OnOutput oldEventOnOutput) + { + var localOutputList = new List + { + oldEventOnOutput.Output, + Output + }; + + var localOutputTextSpanAndOffsetTupleList = new List<(int OutputOffset, List OutputTextSpan)> + { + (oldEventOnOutput.OutputOffset, oldEventOnOutput._outputTextSpanList), + (OutputOffset, _outputTextSpanList) + }; + + return new OnOutputBatch( + oldEventOnOutput.OutputOffset, + localOutputList, + localOutputTextSpanAndOffsetTupleList, + ResourceUri, + TextEditorService, + _terminalCommandBoundary, + ViewModelKey); + } + + if (oldEvent is OnOutputBatch oldOnOutputBatch) + { + oldOnOutputBatch.OutputList.Add(Output); + oldOnOutputBatch.OutputTextSpanAndOffsetTupleList.Add((OutputOffset, _outputTextSpanList)); + return oldOnOutputBatch; + } + + return null; + } + + public Task HandleEvent(CancellationToken cancellationToken) + { + throw new NotImplementedException($"{nameof(ITextEditorTask)} should not implement {nameof(HandleEvent)}" + + "because they instead are contained within an 'IBackgroundTask' that came from the 'TextEditorService'"); + } +} diff --git a/Source/Lib/Ide/Ide.RazorLib/Events/Models/OnOutputBatch.cs b/Source/Lib/Ide/Ide.RazorLib/Events/Models/OnOutputBatch.cs new file mode 100644 index 000000000..352f23ed5 --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Events/Models/OnOutputBatch.cs @@ -0,0 +1,89 @@ +using Luthetus.Common.RazorLib.BackgroundTasks.Models; +using Luthetus.Common.RazorLib.Keys.Models; +using Luthetus.Ide.RazorLib.Terminals.Models; +using Luthetus.TextEditor.RazorLib.Lexes.Models; +using Luthetus.TextEditor.RazorLib; +using Luthetus.TextEditor.RazorLib.TextEditors.Displays; +using Luthetus.TextEditor.RazorLib.TextEditors.Models; + +namespace Luthetus.Ide.RazorLib.Events.Models; + +/// +/// This class is for use with command line text output. +/// For example, every time a line of output is written to stdout +/// one could furthermore invoke this event to write to the text editor +/// in a batched manner. +/// +public class OnOutputBatch : ITextEditorTask +{ + private readonly TerminalCommandBoundary _terminalCommandBoundary; + + public OnOutputBatch( + int batchOutputOffset, + List outputList, + List<(int OutputOffset, List OutputTextSpan)> outputTextSpanAndOffsetTupleList, + ResourceUri resourceUri, + ITextEditorService textEditorService, + TerminalCommandBoundary terminalCommandBoundary, + Key viewModelKey) + { + BatchOutputOffset = batchOutputOffset; + OutputList = outputList; + OutputTextSpanAndOffsetTupleList = outputTextSpanAndOffsetTupleList; + ResourceUri = resourceUri; + TextEditorService = textEditorService; + _terminalCommandBoundary = terminalCommandBoundary; + ViewModelKey = viewModelKey; + } + + + public Key BackgroundTaskKey { get; } = Key.NewKey(); + public Key QueueKey { get; } = ContinuousBackgroundTaskWorker.GetQueueKey(); + public string Name { get; } = nameof(OnOutput); + public Task? WorkProgress { get; } + public int BatchOutputOffset { get; } + public List OutputList { get; } + public List<(int OutputOffset, List OutputTextSpanList)> OutputTextSpanAndOffsetTupleList { get; } + public ResourceUri ResourceUri { get; } + public ITextEditorService TextEditorService { get; } + public Key ViewModelKey { get; } + + public TimeSpan ThrottleTimeSpan => TextEditorViewModelDisplay.TextEditorEvents.ThrottleDelayDefault; + + public Task InvokeWithEditContext(IEditContext editContext) + { + // Flatten 'OutputTextSpanAndOffsetTupleList' + var outputTextSpanList = new List(); + foreach (var tuple in OutputTextSpanAndOffsetTupleList) + { + outputTextSpanList.AddRange(tuple.OutputTextSpanList.Select(x => x with + { + StartingIndexInclusive = x.StartingIndexInclusive + tuple.OutputOffset - BatchOutputOffset, + EndingIndexExclusive = x.EndingIndexExclusive + tuple.OutputOffset - BatchOutputOffset, + })); + } + + var onOutput = new OnOutput( + BatchOutputOffset, + string.Join(string.Empty, OutputList), + outputTextSpanList, + ResourceUri, + TextEditorService, + _terminalCommandBoundary, + ViewModelKey); + + return onOutput.InvokeWithEditContext(editContext); + } + + public IBackgroundTask? BatchOrDefault(IBackgroundTask oldEvent) + { + return null; + } + + public Task HandleEvent(CancellationToken cancellationToken) + { + throw new NotImplementedException($"{nameof(ITextEditorTask)} should not implement {nameof(HandleEvent)}" + + "because they instead are contained within an 'IBackgroundTask' that came from the 'TextEditorService'"); + } +} + diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitAddRepoDisplay.razor b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitAddRepoDisplay.razor new file mode 100644 index 000000000..260ea25d0 --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitAddRepoDisplay.razor @@ -0,0 +1,25 @@ +
+
+ Repo Folder (absolute path): +
+ +
+ + + + +
+
+ + \ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitAddRepoDisplay.razor.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitAddRepoDisplay.razor.cs new file mode 100644 index 000000000..14fc90f15 --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitAddRepoDisplay.razor.cs @@ -0,0 +1,91 @@ +using Fluxor; +using Luthetus.Common.RazorLib.ComponentRenderers.Models; +using Luthetus.Common.RazorLib.Dialogs.States; +using Luthetus.Common.RazorLib.Dynamics.Models; +using Luthetus.Common.RazorLib.FileSystems.Models; +using Luthetus.Common.RazorLib.Notifications.Models; +using Luthetus.Ide.RazorLib.Gits.Models; +using Luthetus.Ide.RazorLib.Gits.States; +using Luthetus.Ide.RazorLib.InputFiles.Models; +using Luthetus.Ide.RazorLib.InputFiles.States; +using Microsoft.AspNetCore.Components; +using System.Collections.Immutable; + +namespace Luthetus.Ide.RazorLib.Gits.Displays; + +public partial class GitAddRepoDisplay : ComponentBase +{ + [Inject] + private InputFileSync InputFileSync { get; set; } = null!; + [Inject] + private IDispatcher Dispatcher { get; set; } = null!; + [Inject] + private IEnvironmentProvider EnvironmentProvider { get; set; } = null!; + [Inject] + private ILuthetusCommonComponentRenderers CommonComponentRenderers { get; set; } = null!; + + [CascadingParameter] + public IDialog Dialog { get; set; } = null!; + + private string _repoAbsolutePathString = string.Empty; + + /// + /// This method allows for a user to pick the '.git' folder, OR a directory.

+ /// + /// Case '.git' is picked:
+ /// The repo will be the parent directory of the '.git' folder.

+ /// + /// Case a directory is picked:
+ /// If directory contains a '.git' folder, then begin using the CLI to get data about the repo.
+ /// Else if directory does NOT contain a '.git' folder, then promp the user to run 'git init'. + ///
+ private void RequestInputFileForGitFolder() + { + InputFileSync.RequestInputFileStateForm("Git Repo", + async absolutePath => + { + if (absolutePath is null) + return; + + if (absolutePath.NameNoExtension == ".git") + { + if (absolutePath.ParentDirectory is null) + { + NotificationHelper.DispatchError( + $"ERROR: {nameof(RequestInputFileForGitFolder)}", + "'.git' folder did not have a parent directory.", + CommonComponentRenderers, + Dispatcher, + TimeSpan.FromSeconds(10)); + return; + } + + absolutePath = EnvironmentProvider.AbsolutePathFactory( + absolutePath.ParentDirectory.Value, + true); + } + + _repoAbsolutePathString = absolutePath.Value; + await InvokeAsync(StateHasChanged); + }, + absolutePath => + { + if (absolutePath is null || !absolutePath.IsDirectory) + return Task.FromResult(false); + + return Task.FromResult(true); + }, + new[] + { + new InputFilePattern("Directory", absolutePath => absolutePath.IsDirectory) + }.ToImmutableArray()); + } + + private void ConfirmGitFolderOnClick() + { + var repoAbsolutePath = EnvironmentProvider.AbsolutePathFactory(_repoAbsolutePathString, true); + Dispatcher.Dispatch(new GitState.SetRepoAction(new GitRepo(repoAbsolutePath))); + + Dispatcher.Dispatch(new DialogState.DisposeAction(Dialog.DynamicViewModelKey)); + } +} \ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitChangesContextMenu.razor b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitChangesContextMenu.razor new file mode 100644 index 000000000..810dd8630 --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitChangesContextMenu.razor @@ -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 +{ + + + +} diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitChangesContextMenu.razor.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitChangesContextMenu.razor.cs new file mode 100644 index 000000000..7f382fd64 --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitChangesContextMenu.razor.cs @@ -0,0 +1,86 @@ +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.Common.RazorLib.BackgroundTasks.Models; +using Luthetus.Ide.RazorLib.Gits.States; + +namespace Luthetus.Ide.RazorLib.Gits.Displays; + +public partial class GitChangesContextMenu : ComponentBase +{ + [Inject] + private IState 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 ContextMenuEventDropdownKey = Key.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 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(); + + if (!menuRecordsList.Any()) + return MenuRecord.Empty; + + return new MenuRecord(menuRecordsList.ToImmutableArray()); + } + + private async Task GetMultiSelectionMenuRecord(TreeViewCommandArgs commandArgs) + { + var menuOptionRecordList = new List(); + Func 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}"; + } +} \ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitChangesTreeViewDisplay.razor b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitChangesTreeViewDisplay.razor new file mode 100644 index 000000000..96c06942e --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitChangesTreeViewDisplay.razor @@ -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 + +
+ + @{ var appOptionsState = AppOptionsStateWrap.Value; } + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitChangesTreeViewDisplay.razor.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitChangesTreeViewDisplay.razor.cs new file mode 100644 index 000000000..c8ecb5148 --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitChangesTreeViewDisplay.razor.cs @@ -0,0 +1,70 @@ +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.Models; +using Luthetus.Ide.RazorLib.Gits.States; +using Microsoft.AspNetCore.Components; + +namespace Luthetus.Ide.RazorLib.Gits.Displays; + +public partial class GitChangesTreeViewDisplay : ComponentBase +{ + /// + /// Awkwardly, the constructor needs this, + /// meanwhile this component is receiving as a cascading parameter. + /// This should be written differently (2024-05-02). + /// + [Inject] + private IState GitStateWrap { get; set; } = null!; + [Inject] + private ITreeViewService TreeViewService { get; set; } = null!; + [Inject] + private IDispatcher Dispatcher { get; set; } = null!; + [Inject] + private IState AppOptionsStateWrap { get; set; } = null!; + [Inject] + private IBackgroundTaskService BackgroundTaskService { get; set; } = null!; + + [CascadingParameter] + public GitState GitState { get; set; } = null!; + + private TreeViewCommandArgs? _mostRecentTreeViewCommandArgs; + private GitTreeViewKeyboardEventHandler _treeViewKeyboardEventHandler = null!; + private GitTreeViewMouseEventHandler _treeViewMouseEventHandler = null!; + + private int OffsetPerDepthInPixels => (int)Math.Ceiling( + AppOptionsStateWrap.Value.Options.IconSizeInPixels * (2.0 / 3.0)); + + protected override void OnInitialized() + { + _treeViewKeyboardEventHandler = new GitTreeViewKeyboardEventHandler( + TreeViewService, + BackgroundTaskService, + GitStateWrap, + Dispatcher); + + _treeViewMouseEventHandler = new GitTreeViewMouseEventHandler( + TreeViewService, + BackgroundTaskService, + GitStateWrap, + Dispatcher); + + 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)); + } +} \ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitControlsDisplay.razor b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitControlsDisplay.razor new file mode 100644 index 000000000..cc24ee9df --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitControlsDisplay.razor @@ -0,0 +1,26 @@ +
+ + @{ var localGitState = GitState; } + + + + + + + + + + +
\ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitControlsDisplay.razor.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitControlsDisplay.razor.cs new file mode 100644 index 000000000..2ca61102d --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitControlsDisplay.razor.cs @@ -0,0 +1,151 @@ +using Fluxor; +using Luthetus.Common.RazorLib.Dialogs.Models; +using Luthetus.Common.RazorLib.Dialogs.States; +using Luthetus.Common.RazorLib.Dynamics.Models; +using Luthetus.Common.RazorLib.FileSystems.Models; +using Luthetus.Common.RazorLib.Keys.Models; +using Luthetus.Ide.RazorLib.CommandLines.Models; +using Luthetus.Ide.RazorLib.Gits.States; +using Luthetus.Ide.RazorLib.Terminals.Models; +using Luthetus.Ide.RazorLib.Terminals.States; +using Microsoft.AspNetCore.Components; +using System.Text; + +namespace Luthetus.Ide.RazorLib.Gits.Displays; + +public partial class GitControlsDisplay : ComponentBase +{ + [Inject] + private IState TerminalStateWrap { get; set; } = null!; + [Inject] + private IDispatcher Dispatcher { get; set; } = null!; + [Inject] + private IEnvironmentProvider EnvironmentProvider { get; set; } = null!; + + [CascadingParameter] + public GitState GitState { get; set; } = null!; + + private string _summary = string.Empty; + + public Key GitStatusTerminalCommandKey { get; } = Key.NewKey(); + public Key GitCommitTerminalCommandKey { get; } = Key.NewKey(); + + private async Task ExecuteGitStatusTerminalCommandOnClick() + { + var localGitState = GitState; + + if (localGitState.Repo is null) + return; + + var gitStatusDashUCommand = $"{GitCliFacts.STATUS_COMMAND} -u"; + var formattedCommand = new FormattedCommand( + GitCliFacts.TARGET_FILE_NAME, + new string[] { gitStatusDashUCommand }) + { + HACK_ArgumentsString = gitStatusDashUCommand + }; + + var gitCliOutputParser = new GitCliOutputParser( + Dispatcher, + localGitState, + EnvironmentProvider); + + var gitStatusCommand = new TerminalCommand( + GitStatusTerminalCommandKey, + formattedCommand, + localGitState.Repo.AbsolutePath.Value, + OutputParser: gitCliOutputParser); + + var generalTerminal = TerminalStateWrap.Value.TerminalMap[TerminalFacts.GENERAL_TERMINAL_KEY]; + await generalTerminal.EnqueueCommandAsync(gitStatusCommand); + } + + private async Task SubmitOnClick(GitState localGitState) + { + var localSummary = _summary; + if (string.IsNullOrWhiteSpace(localSummary)) + return; + + if (localGitState.Repo is null) + return; + + var filesBuilder = new StringBuilder(); + + foreach (var fileAbsolutePath in localGitState.StagedFileMap.Values) + { + var relativePathString = PathHelper.GetRelativeFromTwoAbsolutes( + localGitState.Repo.AbsolutePath, + fileAbsolutePath.AbsolutePath, + EnvironmentProvider); + + if (EnvironmentProvider.DirectorySeparatorChar == '\\') + { + // The following fails: + // git add ".\MyApp\" + // + // Whereas the following succeeds + // git add "./MyApp/" + relativePathString = relativePathString.Replace( + EnvironmentProvider.DirectorySeparatorChar, + EnvironmentProvider.AltDirectorySeparatorChar); + } + + filesBuilder.Append($"\"{relativePathString}\" "); + } + + var argumentsString = "add " + filesBuilder.ToString(); + + var formattedCommand = new FormattedCommand( + GitCliFacts.TARGET_FILE_NAME, + new string[] { argumentsString }) + { + HACK_ArgumentsString = argumentsString + }; + + var gitAddCommand = new TerminalCommand( + GitStatusTerminalCommandKey, + formattedCommand, + localGitState.Repo.AbsolutePath.Value, + ContinueWith: () => CommitChanges(localGitState, localSummary)); + + var generalTerminal = TerminalStateWrap.Value.TerminalMap[TerminalFacts.GENERAL_TERMINAL_KEY]; + await generalTerminal.EnqueueCommandAsync(gitAddCommand); + } + + private async Task CommitChanges(GitState localGitState, string localSummary) + { + if (localGitState.Repo is null) + return; + + var argumentsString = $"commit -m \"{localSummary}\""; + + var formattedCommand = new FormattedCommand( + GitCliFacts.TARGET_FILE_NAME, + new string[] { argumentsString }) + { + HACK_ArgumentsString = argumentsString + }; + + var gitCommitCommand = new TerminalCommand( + GitCommitTerminalCommandKey, + formattedCommand, + localGitState.Repo.AbsolutePath.Value, + ContinueWith: ExecuteGitStatusTerminalCommandOnClick); + + var generalTerminal = TerminalStateWrap.Value.TerminalMap[TerminalFacts.GENERAL_TERMINAL_KEY]; + await generalTerminal.EnqueueCommandAsync(gitCommitCommand); + } + + private void ShowGitOriginDialogOnClick() + { + var dialogViewModel = new DialogViewModel( + Key.NewKey(), + $"Git Origin", + typeof(GitOriginDisplay), + null, + null, + true); + + Dispatcher.Dispatch(new DialogState.RegisterAction(dialogViewModel)); + } +} \ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitDiffDisplay.razor b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitDiffDisplay.razor new file mode 100644 index 000000000..dc533a597 --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitDiffDisplay.razor @@ -0,0 +1,7 @@ +
+ GitDiffDisplay + + +
diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitDiffDisplay.razor.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitDiffDisplay.razor.cs new file mode 100644 index 000000000..04e7aa9de --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitDiffDisplay.razor.cs @@ -0,0 +1,53 @@ +using Fluxor; +using Luthetus.Common.RazorLib.FileSystems.Models; +using Luthetus.Common.RazorLib.Keys.Models; +using Luthetus.Ide.RazorLib.CommandLines.Models; +using Luthetus.Ide.RazorLib.Gits.Models; +using Luthetus.Ide.RazorLib.Gits.States; +using Luthetus.Ide.RazorLib.Terminals.Models; +using Luthetus.Ide.RazorLib.Terminals.States; +using Microsoft.AspNetCore.Components; + +namespace Luthetus.Ide.RazorLib.Gits.Displays; + +public partial class GitDiffDisplay : ComponentBase +{ + [Inject] + private IState TerminalStateWrap { get; set; } = null!; + [Inject] + private IDispatcher Dispatcher { get; set; } = null!; + [Inject] + private IEnvironmentProvider EnvironmentProvider { get; set; } = null!; + [Inject] + private IState GitStateWrap { get; set; } = null!; + + [Parameter, EditorRequired] + public GitFile GitFile { get; set; } = null!; + + public Key GitLogTerminalCommandKey { get; } = Key.NewKey(); + + private async Task ShowOriginalFromGitOnClick() + { + var localGitState = GitStateWrap.Value; + var localGitFile = GitFile; + + if (localGitState.Repo is null) + return; + + var gitLogArgs = $"log -p {localGitFile.RelativePathString}"; + var formattedCommand = new FormattedCommand( + GitCliFacts.TARGET_FILE_NAME, + new string[] { gitLogArgs }) + { + HACK_ArgumentsString = gitLogArgs + }; + + var gitStatusCommand = new TerminalCommand( + GitLogTerminalCommandKey, + formattedCommand, + localGitState.Repo.AbsolutePath.Value); + + var generalTerminal = TerminalStateWrap.Value.TerminalMap[TerminalFacts.GENERAL_TERMINAL_KEY]; + await generalTerminal.EnqueueCommandAsync(gitStatusCommand); + } +} \ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitDisplay.razor b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitDisplay.razor new file mode 100644 index 000000000..1ed266da0 --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitDisplay.razor @@ -0,0 +1,31 @@ +@inherits Fluxor.Blazor.Web.Components.FluxorComponent + +@using Luthetus.Common.RazorLib.Contexts.Displays +@using Luthetus.Common.RazorLib.Contexts.Models + + + + @{ var localGitState = GitStateWrap.Value; } + +
+ Git +
+ +
+ + + + + @if (localGitState.Repo is not null) + { +
+ @localGitState.Repo.AbsolutePath.NameNoExtension +
+ + + } +
+
+
\ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitDisplay.razor.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitDisplay.razor.cs new file mode 100644 index 000000000..e2a7c6217 --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitDisplay.razor.cs @@ -0,0 +1,16 @@ +using Fluxor; +using Fluxor.Blazor.Web.Components; +using Luthetus.Ide.RazorLib.Gits.States; +using Microsoft.AspNetCore.Components; + +namespace Luthetus.Ide.RazorLib.Gits.Displays; + +/// +/// Long term goal is to support any arbitrary version control system. +/// For now, implement a Git UI, this lets us get a feel for what the interface should be. +/// +public partial class GitDisplay : FluxorComponent +{ + [Inject] + private IState GitStateWrap { get; set; } = null!; +} \ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitOriginDisplay.razor b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitOriginDisplay.razor new file mode 100644 index 000000000..daee7c5de --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitOriginDisplay.razor @@ -0,0 +1,41 @@ +
+ + @{ var localGitState = GitStateWrap.Value; } + + + +
+ + Origin: + + @if (string.IsNullOrWhiteSpace(localGitState.Origin)) + { + @: string.IsNullOrWhiteSpace(localGitState.Origin) + } + else + { + @localGitState.Origin + } + +
+ +
+ +
+ @{ var localCommandArgs = CommandArgs; } + + @localCommandArgs +
+ + +
\ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitOriginDisplay.razor.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitOriginDisplay.razor.cs new file mode 100644 index 000000000..d2fea07ac --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitOriginDisplay.razor.cs @@ -0,0 +1,88 @@ +using Fluxor; +using Luthetus.Common.RazorLib.FileSystems.Models; +using Luthetus.Common.RazorLib.Keys.Models; +using Luthetus.Ide.RazorLib.CommandLines.Models; +using Luthetus.Ide.RazorLib.Gits.States; +using Luthetus.Ide.RazorLib.Terminals.Models; +using Luthetus.Ide.RazorLib.Terminals.States; +using Microsoft.AspNetCore.Components; + +namespace Luthetus.Ide.RazorLib.Gits.Displays; + +public partial class GitOriginDisplay : ComponentBase +{ + [Inject] + private IState GitStateWrap { get; set; } = null!; + [Inject] + private IState TerminalStateWrap { get; set; } = null!; + [Inject] + private IDispatcher Dispatcher { get; set; } = null!; + [Inject] + private IEnvironmentProvider EnvironmentProvider { get; set; } = null!; + + private string _gitOrigin = string.Empty; + + public Key GitSetOriginTerminalCommandKey { get; } = Key.NewKey(); + + private string CommandArgs => $"remote add origin \"{_gitOrigin}\""; + + private async Task GetOriginOnClick() + { + var localGitState = GitStateWrap.Value; + + if (localGitState.Repo is null) + return; + + var localCommandArgs = "config --get remote.origin.url"; + var formattedCommand = new FormattedCommand( + GitCliFacts.TARGET_FILE_NAME, + new string[] { localCommandArgs }) + { + HACK_ArgumentsString = localCommandArgs + }; + + var gitCliOutputParser = new GitCliOutputParser( + Dispatcher, + localGitState, + EnvironmentProvider, + stageKind: GitCliOutputParser.StageKind.GetOrigin); + + var gitStatusCommand = new TerminalCommand( + GitSetOriginTerminalCommandKey, + formattedCommand, + localGitState.Repo.AbsolutePath.Value, + OutputParser: gitCliOutputParser); + + var generalTerminal = TerminalStateWrap.Value.TerminalMap[TerminalFacts.GENERAL_TERMINAL_KEY]; + await generalTerminal.EnqueueCommandAsync(gitStatusCommand); + } + + private async Task SetGitOriginOnClick(string localCommandArgs) + { + var localGitState = GitStateWrap.Value; + + if (localGitState.Repo is null) + return; + + var formattedCommand = new FormattedCommand( + GitCliFacts.TARGET_FILE_NAME, + new string[] { localCommandArgs }) + { + HACK_ArgumentsString = localCommandArgs + }; + + var gitCliOutputParser = new GitCliOutputParser( + Dispatcher, + localGitState, + EnvironmentProvider); + + var gitStatusCommand = new TerminalCommand( + GitSetOriginTerminalCommandKey, + formattedCommand, + localGitState.Repo.AbsolutePath.Value, + OutputParser: gitCliOutputParser); + + var generalTerminal = TerminalStateWrap.Value.TerminalMap[TerminalFacts.GENERAL_TERMINAL_KEY]; + await generalTerminal.EnqueueCommandAsync(gitStatusCommand); + } +} \ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitPickGitFolderDisplay.razor b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitPickGitFolderDisplay.razor new file mode 100644 index 000000000..e47f6ccfe --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitPickGitFolderDisplay.razor @@ -0,0 +1,14 @@ +
+ + + + +
\ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitPickGitFolderDisplay.razor.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitPickGitFolderDisplay.razor.cs new file mode 100644 index 000000000..89aadccdd --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Displays/GitPickGitFolderDisplay.razor.cs @@ -0,0 +1,27 @@ +using Fluxor; +using Luthetus.Common.RazorLib.Dialogs.Models; +using Luthetus.Common.RazorLib.Dialogs.States; +using Luthetus.Common.RazorLib.Dynamics.Models; +using Luthetus.Common.RazorLib.Keys.Models; +using Microsoft.AspNetCore.Components; + +namespace Luthetus.Ide.RazorLib.Gits.Displays; + +public partial class GitPickGitFolderDisplay : ComponentBase +{ + [Inject] + private IDispatcher Dispatcher { get; set; } = null!; + + private void ShowAddRepoDialogOnClick() + { + var dialogViewModel = new DialogViewModel( + Key.NewKey(), + $"Git Origin", + typeof(GitAddRepoDisplay), + null, + null, + true); + + Dispatcher.Dispatch(new DialogState.RegisterAction(dialogViewModel)); + } +} \ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Models/GitFile.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/Models/GitFile.cs index 5c9f56198..67943b178 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Gits/Models/GitFile.cs +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Models/GitFile.cs @@ -1,10 +1,9 @@ -using Luthetus.Common.RazorLib.Exceptions; -using Luthetus.Common.RazorLib.FileSystems.Models; +using Luthetus.Common.RazorLib.FileSystems.Models; using Luthetus.Ide.RazorLib.Exceptions; namespace Luthetus.Ide.RazorLib.Gits.Models; -public record GitFile(IAbsolutePath AbsolutePath, GitDirtyReason GitDirtyReason) +public record GitFile(IAbsolutePath AbsolutePath, string RelativePathString, GitDirtyReason GitDirtyReason) { public bool IsDirty => GitDirtyReason switch { diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Models/GitRepo.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/Models/GitRepo.cs new file mode 100644 index 000000000..cdfe2f40b --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Models/GitRepo.cs @@ -0,0 +1,8 @@ +using Luthetus.Common.RazorLib.FileSystems.Models; + +namespace Luthetus.Ide.RazorLib.Gits.Models; + +/// +/// TODO: Why am I parsing the CLI output? Can I just look at the '.git' folder itself? (2024-05-04) +/// +public record GitRepo(IAbsolutePath AbsolutePath); diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Models/GitTreeViewKeyboardEventHandler.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/Models/GitTreeViewKeyboardEventHandler.cs new file mode 100644 index 000000000..b77d89a91 --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Models/GitTreeViewKeyboardEventHandler.cs @@ -0,0 +1,69 @@ +using Fluxor; +using Luthetus.Common.RazorLib.BackgroundTasks.Models; +using Luthetus.Common.RazorLib.Commands.Models; +using Luthetus.Common.RazorLib.Keyboards.Models; +using Luthetus.Common.RazorLib.TreeViews.Models; +using Luthetus.Ide.RazorLib.Gits.States; +using Luthetus.Ide.RazorLib.TreeViewImplementations.Models; +using System.Collections.Immutable; + +namespace Luthetus.Ide.RazorLib.Gits.Models; + +public class GitTreeViewKeyboardEventHandler : TreeViewKeyboardEventHandler +{ + private readonly IState _gitStateWrap; + private readonly IDispatcher _dispatcher; + + public GitTreeViewKeyboardEventHandler( + ITreeViewService treeViewService, + IBackgroundTaskService backgroundTaskService, + IState gitStateWrap, + IDispatcher dispatcher) + : base(treeViewService, backgroundTaskService) + { + _gitStateWrap = gitStateWrap; + _dispatcher = dispatcher; + } + + public override void OnKeyDown(TreeViewCommandArgs commandArgs) + { + if (commandArgs.KeyboardEventArgs is null) + return; + + if (commandArgs.KeyboardEventArgs.Code == KeyboardKeyFacts.WhitespaceCodes.SPACE_CODE || + commandArgs.KeyboardEventArgs.Code == KeyboardKeyFacts.WhitespaceCodes.ENTER_CODE) + { + var localGitState = _gitStateWrap.Value; + + _dispatcher.Dispatch(new GitState.WithAction(inState => + { + if (inState.Repo != localGitState.Repo) + { + // Git folder was changed, throw away the result since it is thereby invalid. + return inState; + } + + var outStagedFileMap = new Dictionary(inState.StagedFileMap); + + foreach (var selectedNode in commandArgs.TreeViewContainer.SelectedNodeList) + { + if (selectedNode is TreeViewGitFile treeViewGitFile) + { + var key = treeViewGitFile.Item.AbsolutePath.Value; + + var wasRemoved = outStagedFileMap.Remove(key); + if (!wasRemoved) + outStagedFileMap.Add(key, treeViewGitFile.Item); + } + } + + return inState with + { + StagedFileMap = outStagedFileMap.ToImmutableDictionary() + }; + })); + } + + base.OnKeyDown(commandArgs); + } +} diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/Models/GitTreeViewMouseEventHandler.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/Models/GitTreeViewMouseEventHandler.cs new file mode 100644 index 000000000..e8145b230 --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/Models/GitTreeViewMouseEventHandler.cs @@ -0,0 +1,54 @@ +using Fluxor; +using Luthetus.Common.RazorLib.BackgroundTasks.Models; +using Luthetus.Common.RazorLib.Commands.Models; +using Luthetus.Common.RazorLib.Dialogs.Models; +using Luthetus.Common.RazorLib.Dialogs.States; +using Luthetus.Common.RazorLib.Dynamics.Models; +using Luthetus.Common.RazorLib.Keys.Models; +using Luthetus.Common.RazorLib.TreeViews.Models; +using Luthetus.Ide.RazorLib.Gits.Displays; +using Luthetus.Ide.RazorLib.Gits.States; +using Luthetus.Ide.RazorLib.TreeViewImplementations.Models; + +namespace Luthetus.Ide.RazorLib.Gits.Models; + +public class GitTreeViewMouseEventHandler : TreeViewMouseEventHandler +{ + private readonly IState _gitStateWrap; + private readonly IDispatcher _dispatcher; + + public GitTreeViewMouseEventHandler( + ITreeViewService treeViewService, + IBackgroundTaskService backgroundTaskService, + IState gitStateWrap, + IDispatcher dispatcher) + : base(treeViewService, backgroundTaskService) + { + _gitStateWrap = gitStateWrap; + _dispatcher = dispatcher; + } + + public override void OnDoubleClick(TreeViewCommandArgs commandArgs) + { + base.OnDoubleClick(commandArgs); + + if (commandArgs.NodeThatReceivedMouseEvent is not TreeViewGitFile treeViewGitFile) + return; + + var dialogViewModel = new DialogViewModel( + Key.NewKey(), + $"Diff: {treeViewGitFile.Item.AbsolutePath.NameWithExtension}", + typeof(GitDiffDisplay), + new Dictionary + { + { + nameof(GitDiffDisplay.GitFile), + treeViewGitFile.Item + } + }, + null, + true); + + _dispatcher.Dispatch(new DialogState.RegisterAction(dialogViewModel)); + } +} diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Actions.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Actions.cs index 23e381cbc..1e2d6c845 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Actions.cs +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Actions.cs @@ -1,6 +1,16 @@ -namespace Luthetus.Ide.RazorLib.Gits.States; +using Luthetus.Ide.RazorLib.Gits.Models; +using System.Collections.Immutable; + +namespace Luthetus.Ide.RazorLib.Gits.States; public partial record GitState { - public record SetGitStateWithAction(Func GitStateWithFunc); + /// + /// If the expected path is not the actual path, then the git file list will NOT be changed. + /// + public record SetFileListAction(GitRepo Repo, ImmutableList FileList); + public record SetOriginAction(GitRepo Repo, string Origin); + public record SetRepoAction(GitRepo? Repo); + public record SetSelectedFileListAction(Func, ImmutableDictionary> SetSelectedFileListFunc); + public record WithAction(Func WithFunc); } \ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Effector.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Effector.cs new file mode 100644 index 000000000..97cae9e51 --- /dev/null +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Effector.cs @@ -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 _gitStateWrap; + private readonly ILuthetusIdeComponentRenderers _ideComponentRenderers; + private readonly ITreeViewService _treeViewService; + private readonly IThrottle _throttle = new Throttle(TimeSpan.FromMilliseconds(300)); + + public Effector( + IState gitStateWrap, + ILuthetusIdeComponentRenderers ideComponentRenderers, + ITreeViewService treeViewService) + { + _gitStateWrap = gitStateWrap; + _ideComponentRenderers = ideComponentRenderers; + _treeViewService = treeViewService; + } + + [EffectMethod(typeof(SetFileListAction))] + public Task HandleSetGitStateWithAction(IDispatcher dispatcher) + { + // Suppress unused variable warning + _ = dispatcher; + + _throttle.PushEvent(_ => + { + var gitState = _gitStateWrap.Value; + + var treeViewList = gitState.FileList.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() + : 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; + } + } +} diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Main.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Main.cs index 4645d4e00..7eb3950e4 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Main.cs +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Main.cs @@ -1,25 +1,23 @@ 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; -/// -/// The Folder, ".git" may be in the following locations:
-/// -In the context of .NET:
-/// --The folder containing the user selected .NET Solution
-/// --The folder containing the user selected C# Project which is being contained in an adhoc .NET Solution
-/// -In the context of using the folder explorer
-/// --The folder which is user selected.
-///
[FeatureState] public partial record GitState( - IAbsolutePath? GitFolderAbsolutePath, - ImmutableList GitFilesList, - ImmutableList ActiveGitTasks) + GitRepo? Repo, + ImmutableList FileList, + ImmutableDictionary StagedFileMap, + ImmutableList ActiveTasks, + string? Origin) { - public GitState() : this(null, ImmutableList.Empty, ImmutableList.Empty) + public static readonly Key TreeViewGitChangesKey = Key.NewKey(); + + public GitState() + : this(null, ImmutableList.Empty, ImmutableDictionary.Empty, ImmutableList.Empty, null) { } diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Reducer.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Reducer.cs index cc2ad45d8..222d4891c 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Reducer.cs +++ b/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitState.Reducer.cs @@ -1,4 +1,6 @@ using Fluxor; +using Luthetus.Ide.RazorLib.Gits.Models; +using System.Collections.Immutable; namespace Luthetus.Ide.RazorLib.Gits.States; @@ -6,12 +8,62 @@ public partial record GitState { public class Reducer { + [ReducerMethod] + public static GitState ReduceSetGitFileListAction( + GitState inState, + SetFileListAction setGitFileListAction) + { + if (inState.Repo != setGitFileListAction.Repo) + { + // Git folder was changed while the text was being parsed, + // throw away the result since it is thereby invalid. + return inState; + } + + return inState with + { + FileList = setGitFileListAction.FileList + }; + } + + [ReducerMethod] + public static GitState ReduceSetGitOriginAction( + GitState inState, + SetOriginAction setOriginAction) + { + if (inState.Repo != setOriginAction.Repo) + { + // Git folder was changed while the text was being parsed, + // throw away the result since it is thereby invalid. + return inState; + } + + return inState with + { + Origin = setOriginAction.Origin + }; + } + + [ReducerMethod] + public static GitState ReduceSetGitFolderAction( + GitState inState, + SetRepoAction setRepoAction) + { + return inState with + { + Repo = setRepoAction.Repo, + FileList = ImmutableList.Empty, + StagedFileMap = ImmutableDictionary.Empty, + ActiveTasks = ImmutableList.Empty, + }; + } + [ReducerMethod] public static GitState ReduceSetGitStateWithAction( - GitState inGitState, - SetGitStateWithAction setGitStateWithAction) + GitState inState, + WithAction withAction) { - return setGitStateWithAction.GitStateWithFunc.Invoke(inGitState); + return withAction.WithFunc.Invoke(inState); } } } \ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitSync.Constructor.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitSync.Constructor.cs deleted file mode 100644 index e5a5cab5a..000000000 --- a/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitSync.Constructor.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Fluxor; -using Luthetus.Common.RazorLib.BackgroundTasks.Models; -using Luthetus.Common.RazorLib.FileSystems.Models; -using Luthetus.Ide.RazorLib.Terminals.States; - -namespace Luthetus.Ide.RazorLib.Gits.States; - -public partial class GitSync -{ - private readonly IState _terminalStateWrap; - private readonly IState _gitStateWrap; - private readonly IFileSystemProvider _fileSystemProvider; - private readonly IEnvironmentProvider _environmentProvider; - - public GitSync( - IState terminalStateWrap, - IState gitStateWrap, - IFileSystemProvider fileSystemProvider, - IEnvironmentProvider environmentProvider, - IBackgroundTaskService backgroundTaskService, - IDispatcher dispatcher) - { - _terminalStateWrap = terminalStateWrap; - _gitStateWrap = gitStateWrap; - _fileSystemProvider = fileSystemProvider; - _environmentProvider = environmentProvider; - - BackgroundTaskService = backgroundTaskService; - Dispatcher = dispatcher; - } - - public IBackgroundTaskService BackgroundTaskService { get; } - public IDispatcher Dispatcher { get; } -} diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitSync.Enqueues.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitSync.Enqueues.cs deleted file mode 100644 index 03a15834d..000000000 --- a/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitSync.Enqueues.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Luthetus.Common.RazorLib.FileSystems.Models; -using Luthetus.Common.RazorLib.BackgroundTasks.Models; -using Luthetus.Common.RazorLib.Keys.Models; - -namespace Luthetus.Ide.RazorLib.Gits.States; - -public partial class GitSync -{ - public void RefreshGit(CancellationToken cancellationToken) - { - BackgroundTaskService.Enqueue(Key.NewKey(), ContinuousBackgroundTaskWorker.GetQueueKey(), - "Git Refresh", - async () => await RefreshGitAsync(cancellationToken)); - } - - public void GitInit(CancellationToken cancellationToken) - { - BackgroundTaskService.Enqueue(Key.NewKey(), ContinuousBackgroundTaskWorker.GetQueueKey(), - "Git Init", - async () => await GitInitAsync(cancellationToken)); - } - - public void TryFindGitFolderInDirectory( - IAbsolutePath directoryAbsolutePath, - CancellationToken cancellationToken) - { - BackgroundTaskService.Enqueue(Key.NewKey(), ContinuousBackgroundTaskWorker.GetQueueKey(), - "Git Find '.git' Folder", - async () => await TryFindGitFolderInDirectoryAsync(directoryAbsolutePath, cancellationToken)); - } -} diff --git a/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitSync.Tasks.cs b/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitSync.Tasks.cs deleted file mode 100644 index e09c78004..000000000 --- a/Source/Lib/Ide/Ide.RazorLib/Gits/States/GitSync.Tasks.cs +++ /dev/null @@ -1,331 +0,0 @@ -using System.Text; -using System.Collections.Immutable; -using Luthetus.Ide.RazorLib.Gits.Models; -using Luthetus.Common.RazorLib.FileSystems.Models; -using Luthetus.Ide.RazorLib.CommandLines.Models; -using Luthetus.Ide.RazorLib.Terminals.Models; -using static Luthetus.Ide.RazorLib.Gits.States.GitState; - -namespace Luthetus.Ide.RazorLib.Gits.States; - -public partial class GitSync -{ - private async Task RefreshGitAsync(CancellationToken cancellationToken) - { - var handleRefreshGitTask = new GitTask( - Guid.NewGuid(), - nameof(RefreshGit), - cancellationToken); - - if (cancellationToken.IsCancellationRequested) - return; - - var gitState = _gitStateWrap.Value; - - Dispatcher.Dispatch(new SetGitStateWithAction(inGitState => - { - var nextActiveGitTasks = inGitState.ActiveGitTasks.Add(handleRefreshGitTask); - return inGitState with { ActiveGitTasks = nextActiveGitTasks }; - })); - - // Do not combine this following Dispatch for GitFilesList replacement - // with the Dispatch for ActiveGitTasks replacement. - // It could cause confusion in the future when one gets removed - // without realizing the other was also part of the Dispatch replacement. - Dispatcher.Dispatch(new SetGitStateWithAction(inGitState => inGitState with - { - GitFilesList = ImmutableList.Empty - })); - - if (gitState.GitFolderAbsolutePath is null || - !await _fileSystemProvider.Directory.ExistsAsync(gitState.GitFolderAbsolutePath.Value) || - gitState.GitFolderAbsolutePath.ParentDirectory is null) - { - return; - } - - var generalTerminal = _terminalStateWrap.Value.TerminalMap[TerminalFacts.GENERAL_TERMINAL_KEY]; - - var formattedCommand = new FormattedCommand(GitCliFacts.TARGET_FILE_NAME, new[] - { - GitCliFacts.STATUS_COMMAND - }); - - var gitStatusCommand = new TerminalCommand( - GitFacts.GitStatusTerminalCommandKey, - formattedCommand, - gitState.GitFolderAbsolutePath.ParentDirectory.Value, - CancellationToken.None, - async () => - { - var gitStatusOutput = generalTerminal.ReadStandardOut( - GitFacts.GitStatusTerminalCommandKey); - - if (gitStatusOutput is null) - return; - - await GetGitOutputSectionAsync( - gitState, - gitStatusOutput, - GitFacts.UNTRACKED_FILES_TEXT_START, - GitDirtyReason.Untracked, - UntrackedFilesOnAfterCompletedAction); - - await GetGitOutputSectionAsync( - gitState, - gitStatusOutput, - GitFacts.CHANGES_NOT_STAGED_FOR_COMMIT_TEXT_START, - null, - ChangesNotStagedOnAfterCompletedAction); - }); - - await generalTerminal.EnqueueCommandAsync(gitStatusCommand); - - Dispatcher.Dispatch(new SetGitStateWithAction(inGitState => - { - var nextActiveGitTasks = inGitState.ActiveGitTasks.Remove(handleRefreshGitTask); - return inGitState with { ActiveGitTasks = nextActiveGitTasks }; - })); - - void UntrackedFilesOnAfterCompletedAction(ImmutableList gitFiles) - { - Dispatcher.Dispatch(new SetGitStateWithAction(inGitState => - { - var nextGitFilesList = inGitState.GitFilesList.AddRange(gitFiles); - return inGitState with { GitFilesList = nextGitFilesList }; - })); - } - - void ChangesNotStagedOnAfterCompletedAction(ImmutableList gitFiles) - { - Dispatcher.Dispatch(new SetGitStateWithAction(inGitState => - { - var nextGitFilesList = inGitState.GitFilesList.AddRange(gitFiles); - return inGitState with { GitFilesList = nextGitFilesList }; - })); - } - } - - private async Task GitInitAsync(CancellationToken cancellationToken) - { - var handleHandleGitInitAction = new GitTask( - Guid.NewGuid(), - nameof(RefreshGit), - cancellationToken); - - if (cancellationToken.IsCancellationRequested) - return; - - Dispatcher.Dispatch(new SetGitStateWithAction(inGitState => - { - var nextActiveGitTasks = inGitState.ActiveGitTasks.Add(handleHandleGitInitAction); - return inGitState with { ActiveGitTasks = nextActiveGitTasks }; - })); - - var gitState = _gitStateWrap.Value; - - if (gitState.GitFolderAbsolutePath is null) - return; - - var formattedCommand = new FormattedCommand(GitCliFacts.TARGET_FILE_NAME, new[] - { - GitCliFacts.INIT_COMMAND - }); - - var gitInitCommand = new TerminalCommand( - GitFacts.GitInitTerminalCommandKey, - formattedCommand, - gitState.GitFolderAbsolutePath.Value, - CancellationToken.None, - async () => await RefreshGitAsync(cancellationToken)); - - var generalTerminal = _terminalStateWrap.Value.TerminalMap[TerminalFacts.GENERAL_TERMINAL_KEY]; - await generalTerminal.EnqueueCommandAsync(gitInitCommand); - - Dispatcher.Dispatch(new SetGitStateWithAction(inGitState => - { - var nextActiveGitTasks = inGitState.ActiveGitTasks.Remove(handleHandleGitInitAction); - return inGitState with { ActiveGitTasks = nextActiveGitTasks }; - })); - } - - private async Task TryFindGitFolderInDirectoryAsync( - IAbsolutePath directoryAbsolutePath, - CancellationToken cancellationToken) - { - var handleTryFindGitFolderInDirectoryAction = new GitTask( - Guid.NewGuid(), - nameof(RefreshGit), - cancellationToken); - - if (cancellationToken.IsCancellationRequested) - return; - - Dispatcher.Dispatch(new SetGitStateWithAction(inGitState => - { - var nextActiveGitTasks = inGitState.ActiveGitTasks.Add(handleTryFindGitFolderInDirectoryAction); - return inGitState with { ActiveGitTasks = nextActiveGitTasks }; - })); - - if (!directoryAbsolutePath.IsDirectory) - return; - - var directoryAbsolutePathString = directoryAbsolutePath.Value; - - var childDirectoryAbsolutePathStrings = await _fileSystemProvider.Directory.GetDirectoriesAsync( - directoryAbsolutePathString); - - var gitFolderAbsolutePathString = childDirectoryAbsolutePathStrings.FirstOrDefault( - x => x.EndsWith(GitFacts.GIT_FOLDER_NAME)); - - if (gitFolderAbsolutePathString is not null) - { - var gitFolderAbsolutePath = _environmentProvider.AbsolutePathFactory( - gitFolderAbsolutePathString, - true); - - Dispatcher.Dispatch(new SetGitStateWithAction(inGitState => inGitState with - { - GitFolderAbsolutePath = gitFolderAbsolutePath - })); - - await RefreshGitAsync(cancellationToken); - } - else - { - Dispatcher.Dispatch(new SetGitStateWithAction(inGitState => inGitState with - { - GitFolderAbsolutePath = null, - })); - } - Dispatcher.Dispatch(new SetGitStateWithAction(inGitState => - { - var nextActiveGitTasks = inGitState.ActiveGitTasks.Remove(handleTryFindGitFolderInDirectoryAction); - return inGitState with { ActiveGitTasks = nextActiveGitTasks }; - })); - } - - private async Task GetGitOutputSectionAsync( - GitState gitState, - string gitStatusOutput, - string sectionStart, - GitDirtyReason? gitDirtyReason, - Action> onAfterCompletedAction) - { - if (gitState.GitFolderAbsolutePath?.ParentDirectory is null) - return; - - var indexOfChangesNotStagedForCommitTextStart = gitStatusOutput.IndexOf( - sectionStart, - StringComparison.InvariantCulture); - - if (indexOfChangesNotStagedForCommitTextStart != -1) - { - var startOfChangesNotStagedForCommitIndex = indexOfChangesNotStagedForCommitTextStart + - sectionStart.Length; - - var gitStatusOutputReader = new StringReader( - gitStatusOutput.Substring(startOfChangesNotStagedForCommitIndex)); - - var changesNotStagedForCommitBuilder = new StringBuilder(); - - // This skips the second newline when seeing: "\n\n" - string? currentLine = await gitStatusOutputReader.ReadLineAsync(); - - while ((currentLine = await gitStatusOutputReader.ReadLineAsync()) is not null && - currentLine.Length > 0) - { - // It is presumed that git CLI provides comments on lines - // which start with two space characters. - // - // Whereas output for this command starts with a tab. - // - // TODO: I imagine this is a very naive presumption and this should be revisited but I am still feeling out how to write this git logic. - - /* - * Changes not staged for commit: - * (use "git add/rm ..." to update what will be committed) - * (use "git restore ..." to discard changes in working directory) - * modified: BlazorCrudApp.ServerSide/Pages/Counter.razor - * deleted: BlazorCrudApp.ServerSide/Shared/SurveyPrompt.razor - */ - if (currentLine.StartsWith(new string(' ', 2))) - continue; - - changesNotStagedForCommitBuilder.Append(currentLine); - } - - var changesNotStagedForCommitText = changesNotStagedForCommitBuilder.ToString(); - - var changesNotStagedForCommitCollection = changesNotStagedForCommitText - .Split('\t') - .Select(x => x.Trim()) - .OrderBy(x => x) - .ToArray(); - - if (changesNotStagedForCommitCollection.First() == string.Empty) - { - changesNotStagedForCommitCollection = changesNotStagedForCommitCollection - .Skip(1) - .ToArray(); - } - - (string relativePath, GitDirtyReason gitDirtyReason)[] changesNotStagedForCommitRelativePathAndGitDirtyReasonTuples; - - if (gitDirtyReason is not null) - { - changesNotStagedForCommitRelativePathAndGitDirtyReasonTuples = changesNotStagedForCommitCollection - .Select(x => (x, gitDirtyReason.Value)) - .ToArray(); - } - else - { - changesNotStagedForCommitRelativePathAndGitDirtyReasonTuples = changesNotStagedForCommitCollection - .Select(x => - { - var relativePath = x; - GitDirtyReason innerGitDirtyReason = GitDirtyReason.None; - - if (x.StartsWith(GitFacts.GIT_DIRTY_REASON_MODIFIED)) - { - innerGitDirtyReason = GitDirtyReason.Modified; - - relativePath = new string(relativePath - .Skip(GitFacts.GIT_DIRTY_REASON_MODIFIED.Length) - .ToArray()); - } - else if (x.StartsWith(GitFacts.GIT_DIRTY_REASON_DELETED)) - { - innerGitDirtyReason = GitDirtyReason.Deleted; - - relativePath = new string(relativePath - .Skip(GitFacts.GIT_DIRTY_REASON_DELETED.Length) - .ToArray()); - } - - return (relativePath, innerGitDirtyReason); - }) - .ToArray(); - } - - var changesNotStagedForCommitGitFiles = changesNotStagedForCommitRelativePathAndGitDirtyReasonTuples - .Select(x => - { - var absolutePathString = - gitState.GitFolderAbsolutePath.ParentDirectory + - x.relativePath; - - var isDirectory = _environmentProvider.IsDirectorySeparator(x.relativePath.LastOrDefault()); - - var absolutePath = _environmentProvider.AbsolutePathFactory( - absolutePathString, - isDirectory); - - return new GitFile(absolutePath, x.gitDirtyReason); - }) - .ToImmutableList(); - - onAfterCompletedAction.Invoke(changesNotStagedForCommitGitFiles); - } - } -} diff --git a/Source/Lib/Ide/Ide.RazorLib/InputFiles/Displays/InputFileDisplay.razor.cs b/Source/Lib/Ide/Ide.RazorLib/InputFiles/Displays/InputFileDisplay.razor.cs index 7d64d9de5..338c300e9 100644 --- a/Source/Lib/Ide/Ide.RazorLib/InputFiles/Displays/InputFileDisplay.razor.cs +++ b/Source/Lib/Ide/Ide.RazorLib/InputFiles/Displays/InputFileDisplay.razor.cs @@ -169,6 +169,9 @@ private void InitializeElementDimensions() }); } + /// + /// TODO: This code should be moved to an Effect, of which is throttled. (2024-05-03) + /// private async Task SetInputFileContentTreeViewRootFunc(IAbsolutePath absolutePath) { var pseudoRootNode = new TreeViewAbsolutePath( diff --git a/Source/Lib/Ide/Ide.RazorLib/Installations/Displays/LuthetusIdeInitializer.razor.cs b/Source/Lib/Ide/Ide.RazorLib/Installations/Displays/LuthetusIdeInitializer.razor.cs index ceba70d5f..2263428f4 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Installations/Displays/LuthetusIdeInitializer.razor.cs +++ b/Source/Lib/Ide/Ide.RazorLib/Installations/Displays/LuthetusIdeInitializer.razor.cs @@ -24,13 +24,14 @@ using Luthetus.Common.RazorLib.Dialogs.Models; using Microsoft.JSInterop; using Luthetus.TextEditor.RazorLib; +using Luthetus.Ide.RazorLib.Gits.Displays; namespace Luthetus.Ide.RazorLib.Installations.Displays; public partial class LuthetusIdeInitializer : ComponentBase { [Inject] - private IState PanelsStateWrap { get; set; } = null!; + private IState PanelStateWrap { get; set; } = null!; [Inject] private IDispatcher Dispatcher { get; set; } = null!; [Inject] @@ -115,7 +116,7 @@ private void InitializePanelTabs() private void InitializeLeftPanelTabs() { - var leftPanel = PanelFacts.GetTopLeftPanelGroup(PanelsStateWrap.Value); + var leftPanel = PanelFacts.GetTopLeftPanelGroup(PanelStateWrap.Value); leftPanel.Dispatcher = Dispatcher; // solutionExplorerPanel @@ -129,8 +130,22 @@ private void InitializeLeftPanelTabs() Dispatcher, DialogService, JsRuntime); - Dispatcher.Dispatch(new PanelsState.RegisterPanelAction(solutionExplorerPanel)); - Dispatcher.Dispatch(new PanelsState.RegisterPanelTabAction(leftPanel.Key, solutionExplorerPanel, false)); + Dispatcher.Dispatch(new PanelState.RegisterPanelAction(solutionExplorerPanel)); + Dispatcher.Dispatch(new PanelState.RegisterPanelTabAction(leftPanel.Key, solutionExplorerPanel, false)); + + // gitPanel + var gitPanel = new Panel( + "Git Changes", + Key.NewKey(), + Key.NewKey(), + ContextFacts.GitContext.ContextKey, + typeof(GitDisplay), + null, + Dispatcher, + DialogService, + JsRuntime); + Dispatcher.Dispatch(new PanelState.RegisterPanelAction(gitPanel)); + Dispatcher.Dispatch(new PanelState.RegisterPanelTabAction(leftPanel.Key, gitPanel, false)); // folderExplorerPanel var folderExplorerPanel = new Panel( @@ -143,16 +158,16 @@ private void InitializeLeftPanelTabs() Dispatcher, DialogService, JsRuntime); - Dispatcher.Dispatch(new PanelsState.RegisterPanelAction(folderExplorerPanel)); - Dispatcher.Dispatch(new PanelsState.RegisterPanelTabAction(leftPanel.Key, folderExplorerPanel, false)); + Dispatcher.Dispatch(new PanelState.RegisterPanelAction(folderExplorerPanel)); + Dispatcher.Dispatch(new PanelState.RegisterPanelTabAction(leftPanel.Key, folderExplorerPanel, false)); // SetActivePanelTabAction - Dispatcher.Dispatch(new PanelsState.SetActivePanelTabAction(leftPanel.Key, solutionExplorerPanel.Key)); + Dispatcher.Dispatch(new PanelState.SetActivePanelTabAction(leftPanel.Key, solutionExplorerPanel.Key)); } private void InitializeRightPanelTabs() { - var rightPanel = PanelFacts.GetTopRightPanelGroup(PanelsStateWrap.Value); + var rightPanel = PanelFacts.GetTopRightPanelGroup(PanelStateWrap.Value); rightPanel.Dispatcher = Dispatcher; // compilerServiceExplorerPanel @@ -166,8 +181,8 @@ private void InitializeRightPanelTabs() Dispatcher, DialogService, JsRuntime); - Dispatcher.Dispatch(new PanelsState.RegisterPanelAction(compilerServiceExplorerPanel)); - Dispatcher.Dispatch(new PanelsState.RegisterPanelTabAction(rightPanel.Key, compilerServiceExplorerPanel, false)); + Dispatcher.Dispatch(new PanelState.RegisterPanelAction(compilerServiceExplorerPanel)); + Dispatcher.Dispatch(new PanelState.RegisterPanelTabAction(rightPanel.Key, compilerServiceExplorerPanel, false)); // compilerServiceEditorPanel var compilerServiceEditorPanel = new Panel( @@ -180,8 +195,8 @@ private void InitializeRightPanelTabs() Dispatcher, DialogService, JsRuntime); - Dispatcher.Dispatch(new PanelsState.RegisterPanelAction(compilerServiceEditorPanel)); - Dispatcher.Dispatch(new PanelsState.RegisterPanelTabAction(rightPanel.Key, compilerServiceEditorPanel, false)); + Dispatcher.Dispatch(new PanelState.RegisterPanelAction(compilerServiceEditorPanel)); + Dispatcher.Dispatch(new PanelState.RegisterPanelTabAction(rightPanel.Key, compilerServiceEditorPanel, false)); // TODO: The ITextEditorDiffApi.Calculate method is being commented out as of (2024-02-23). It needs to be re-written... // ...so that it uses the text editor's edit context by using ITextEditorService.Post() @@ -195,13 +210,13 @@ private void InitializeRightPanelTabs() // { // ContextRecordKey = ContextFacts.GitContext.ContextKey // }; - // Dispatcher.Dispatch(new PanelsState.RegisterPanelAction(gitChangesPanel)); - // Dispatcher.Dispatch(new PanelsState.RegisterPanelTabAction(rightPanel.Key, gitChangesPanel, false)); + // Dispatcher.Dispatch(new PanelState.RegisterPanelAction(gitChangesPanel)); + // Dispatcher.Dispatch(new PanelState.RegisterPanelTabAction(rightPanel.Key, gitChangesPanel, false)); } private void InitializeBottomPanelTabs() { - var bottomPanel = PanelFacts.GetBottomPanelGroup(PanelsStateWrap.Value); + var bottomPanel = PanelFacts.GetBottomPanelGroup(PanelStateWrap.Value); bottomPanel.Dispatcher = Dispatcher; // terminalGroupPanel @@ -215,8 +230,8 @@ private void InitializeBottomPanelTabs() Dispatcher, DialogService, JsRuntime); - Dispatcher.Dispatch(new PanelsState.RegisterPanelAction(terminalGroupPanel)); - Dispatcher.Dispatch(new PanelsState.RegisterPanelTabAction(bottomPanel.Key, terminalGroupPanel, false)); + Dispatcher.Dispatch(new PanelState.RegisterPanelAction(terminalGroupPanel)); + Dispatcher.Dispatch(new PanelState.RegisterPanelTabAction(bottomPanel.Key, terminalGroupPanel, false)); // nuGetPanel var nuGetPanel = new Panel( @@ -229,8 +244,8 @@ private void InitializeBottomPanelTabs() Dispatcher, DialogService, JsRuntime); - Dispatcher.Dispatch(new PanelsState.RegisterPanelAction(nuGetPanel)); - Dispatcher.Dispatch(new PanelsState.RegisterPanelTabAction(bottomPanel.Key, nuGetPanel, false)); + Dispatcher.Dispatch(new PanelState.RegisterPanelAction(nuGetPanel)); + Dispatcher.Dispatch(new PanelState.RegisterPanelTabAction(bottomPanel.Key, nuGetPanel, false)); // activeContextsPanel var activeContextsPanel = new Panel( @@ -243,8 +258,8 @@ private void InitializeBottomPanelTabs() Dispatcher, DialogService, JsRuntime); - Dispatcher.Dispatch(new PanelsState.RegisterPanelAction(activeContextsPanel)); - Dispatcher.Dispatch(new PanelsState.RegisterPanelTabAction(bottomPanel.Key, activeContextsPanel, false)); + Dispatcher.Dispatch(new PanelState.RegisterPanelAction(activeContextsPanel)); + Dispatcher.Dispatch(new PanelState.RegisterPanelTabAction(bottomPanel.Key, activeContextsPanel, false)); // testExplorerPanel var testExplorerPanel = new Panel( @@ -257,10 +272,10 @@ private void InitializeBottomPanelTabs() Dispatcher, DialogService, JsRuntime); - Dispatcher.Dispatch(new PanelsState.RegisterPanelAction(testExplorerPanel)); - Dispatcher.Dispatch(new PanelsState.RegisterPanelTabAction(bottomPanel.Key, testExplorerPanel, false)); + Dispatcher.Dispatch(new PanelState.RegisterPanelAction(testExplorerPanel)); + Dispatcher.Dispatch(new PanelState.RegisterPanelTabAction(bottomPanel.Key, testExplorerPanel, false)); // SetActivePanelTabAction - Dispatcher.Dispatch(new PanelsState.SetActivePanelTabAction(bottomPanel.Key, terminalGroupPanel.Key)); + Dispatcher.Dispatch(new PanelState.SetActivePanelTabAction(bottomPanel.Key, terminalGroupPanel.Key)); } } \ No newline at end of file diff --git a/Source/Lib/Ide/Ide.RazorLib/Keymaps/Models/Terminals/TextEditorKeymapTerminal.cs b/Source/Lib/Ide/Ide.RazorLib/Keymaps/Models/Terminals/TextEditorKeymapTerminal.cs index 0a77d57f3..56eb3f62c 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Keymaps/Models/Terminals/TextEditorKeymapTerminal.cs +++ b/Source/Lib/Ide/Ide.RazorLib/Keymaps/Models/Terminals/TextEditorKeymapTerminal.cs @@ -22,7 +22,7 @@ using Luthetus.Ide.RazorLib.Terminals.States; using Luthetus.TextEditor.RazorLib.Lexes.Models; using System.Collections.Immutable; -using Luthetus.TextEditor.RazorLib.Events; +using Luthetus.TextEditor.RazorLib.Events.Models; namespace Luthetus.Ide.RazorLib.Keymaps.Models.Terminals; @@ -142,7 +142,7 @@ public bool TryMap( var input = new TextEditorTextSpan( mostRecentWorkingDirectoryText.EndingIndexExclusive, - modelModifier.DocumentLength, + modelModifier.CharCount, 0, modelModifier.ResourceUri, modelModifier.GetAllText()); @@ -164,12 +164,7 @@ public bool TryMap( var terminalCommand = new TerminalCommand( Key.NewKey(), - formattedCommand, - ContinueWith: () => - { - terminalResource.Terminal.WriteWorkingDirectory(); - return Task.CompletedTask; - }); + formattedCommand); terminalResource.ManualDecorationTextSpanList.Add(terminalResource.TargetFilePathTextSpan); diff --git a/Source/Lib/Ide/Ide.RazorLib/Luthetus.Ide.RazorLib.csproj b/Source/Lib/Ide/Ide.RazorLib/Luthetus.Ide.RazorLib.csproj index bb975b4c7..d8b32706e 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Luthetus.Ide.RazorLib.csproj +++ b/Source/Lib/Ide/Ide.RazorLib/Luthetus.Ide.RazorLib.csproj @@ -1,10 +1,10 @@ - + net6.0 enable enable - 0.8.2 + 0.8.3 @@ -17,8 +17,6 @@ - - diff --git a/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeBody.razor b/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeBody.razor index e43345767..8d8c69a03 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeBody.razor +++ b/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeBody.razor @@ -15,7 +15,7 @@ ReRenderSelfAndAdjacentElementDimensionsFunc="ReRenderLeftPanelAndEditor" /> - @@ -24,7 +24,7 @@ diff --git a/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeBody.razor.cs b/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeBody.razor.cs index 3ef4bbddc..c2ad11a19 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeBody.razor.cs +++ b/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeBody.razor.cs @@ -10,7 +10,7 @@ namespace Luthetus.Ide.RazorLib.Shareds.Displays; public partial class IdeBody : ComponentBase { [Inject] - private IState PanelsStateWrap { get; set; } = null!; + private IState PanelStateWrap { get; set; } = null!; [Parameter, EditorRequired] public ElementDimensions BodyElementDimensions { get; set; } = null!; diff --git a/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeHeader.razor.cs b/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeHeader.razor.cs index 1d0ddfeab..dac440c20 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeHeader.razor.cs +++ b/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeHeader.razor.cs @@ -31,7 +31,7 @@ namespace Luthetus.Ide.RazorLib.Shareds.Displays; public partial class IdeHeader : ComponentBase { [Inject] - private IState PanelsStateWrap { get; set; } = null!; + private IState PanelStateWrap { get; set; } = null!; [Inject] private IState DialogStateWrap { get; set; } = null!; [Inject] @@ -254,10 +254,10 @@ private void InitializeMenuTools() private void InitializeMenuView() { var menuOptionsList = new List(); - var panelsState = PanelsStateWrap.Value; + var panelState = PanelStateWrap.Value; var dialogState = DialogStateWrap.Value; - foreach (var panel in panelsState.PanelList) + foreach (var panel in panelState.PanelList) { var menuOptionPanel = new MenuOptionRecord( panel.Title, @@ -268,7 +268,7 @@ private void InitializeMenuView() if (panelGroup is not null) { - Dispatcher.Dispatch(new PanelsState.SetActivePanelTabAction(panelGroup.Key, panel.Key)); + Dispatcher.Dispatch(new PanelState.SetActivePanelTabAction(panelGroup.Key, panel.Key)); } else { @@ -278,8 +278,8 @@ private void InitializeMenuView() //} //else { - Dispatcher.Dispatch(new PanelsState.RegisterPanelTabAction(PanelFacts.LeftPanelGroupKey, panel, true)); - Dispatcher.Dispatch(new PanelsState.SetActivePanelTabAction(PanelFacts.LeftPanelGroupKey, panel.Key)); + Dispatcher.Dispatch(new PanelState.RegisterPanelTabAction(PanelFacts.LeftPanelGroupKey, panel, true)); + Dispatcher.Dispatch(new PanelState.SetActivePanelTabAction(PanelFacts.LeftPanelGroupKey, panel.Key)); } } diff --git a/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeMainLayout.razor b/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeMainLayout.razor index 07964008d..c879d422b 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeMainLayout.razor +++ b/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/IdeMainLayout.razor @@ -43,7 +43,7 @@ AppOptionsStateWrap { get; set; } = null!; [Inject] - private IState PanelsStateWrap { get; set; } = null!; + private IState PanelStateWrap { get; set; } = null!; [Inject] private IDispatcher Dispatcher { get; set; } = null!; [Inject] @@ -34,6 +42,12 @@ public partial class IdeMainLayout : LayoutComponentBase, IDisposable private IFileSystemProvider FileSystemProvider { get; set; } = null!; [Inject] private DotNetSolutionSync DotNetSolutionSync { get; set; } = null!; + [Inject] + private ILuthetusIdeComponentRenderers IdeComponentRenderers { get; set; } = null!; + [Inject] + private ILuthetusCommonComponentRenderers CommonComponentRenderers { get; set; } = null!; + [Inject] + private ITreeViewService TreeViewService { get; set; } = null!; private bool _previousDragStateWrapShouldDisplay; private ElementDimensions _bodyElementDimensions = new(); @@ -81,29 +95,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) await TextEditorService.OptionsApi.SetFromLocalStorageAsync(); await AppOptionsService.SetFromLocalStorageAsync(); - string personalTestPath; - -#if DEBUG - personalTestPath = "C:\\Users\\hunte\\Repos\\Demos\\BlazorApp4NetCoreDbg\\BlazorApp4NetCoreDbg.sln"; -#else - personalTestPath = "C:\\Users\\hunte\\Repos\\Luthetus.Ide_Fork\\Luthetus.Ide.sln"; -#endif - - if (await FileSystemProvider.File.ExistsAsync(personalTestPath)) - { - var absolutePath = EnvironmentProvider.AbsolutePathFactory( - personalTestPath, - false); - - DotNetSolutionSync.SetDotNetSolution(absolutePath); - -#if DEBUG - Dispatcher.Dispatch(new ProgramExecutionState.SetStartupProjectAbsolutePathAction( - EnvironmentProvider.AbsolutePathFactory( - "C:\\Users\\hunte\\Repos\\Demos\\BlazorApp4NetCoreDbg\\BlazorApp4NetCoreDbg\\BlazorApp4NetCoreDbg.csproj", - false))); -#endif - } + await HACK_PersonalSettings(); } await base.OnAfterRenderAsync(firstRender); @@ -128,6 +120,115 @@ private async void TextEditorOptionsStateWrap_StateChanged(object? sender, Event await InvokeAsync(StateHasChanged); } + /// + /// Personal settings to have closing and reopening the IDE be exactly the state I want while developing. + /// + private async Task HACK_PersonalSettings() + { + string? slnPersonalPath = null; + string? projectPersonalPath = null; +#if DEBUG + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + slnPersonalPath = "/home/hunter/Repos/BlazorCrudApp/BlazorCrudApp.sln"; + projectPersonalPath = "/home/hunter/Repos/BlazorCrudApp/BlazorCrudApp.ServerSide/BlazorCrudApp.ServerSide.csproj"; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + slnPersonalPath = "C:\\Users\\hunte\\Repos\\Demos\\BlazorApp4NetCoreDbg\\BlazorApp4NetCoreDbg.sln"; + projectPersonalPath = "C:\\Users\\hunte\\Repos\\Demos\\BlazorApp4NetCoreDbg\\BlazorApp4NetCoreDbg\\BlazorApp4NetCoreDbg.csproj"; + } +#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + slnPersonalPath = "/home/hunter/Repos/Luthetus.Ide_Fork/Luthetus.Ide.sln"; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + slnPersonalPath = "C:\\Users\\hunte\\Repos\\Luthetus.Ide_Fork\\Luthetus.Ide.sln"; + } +#endif + if (!string.IsNullOrWhiteSpace(slnPersonalPath) && + await FileSystemProvider.File.ExistsAsync(slnPersonalPath)) + { + var slnAbsolutePath = EnvironmentProvider.AbsolutePathFactory( + slnPersonalPath, + false); + + DotNetSolutionSync.SetDotNetSolution(slnAbsolutePath); + + var parentDirectory = slnAbsolutePath.ParentDirectory; + if (parentDirectory is not null) + { + var parentDirectoryAbsolutePath = EnvironmentProvider.AbsolutePathFactory( + parentDirectory.Value, + true); + + var pseudoRootNode = new TreeViewAbsolutePath( + parentDirectoryAbsolutePath, + IdeComponentRenderers, + CommonComponentRenderers, + FileSystemProvider, + EnvironmentProvider, + true, + false); + + await pseudoRootNode.LoadChildListAsync(); + + var adhocRootNode = TreeViewAdhoc.ConstructTreeViewAdhoc(pseudoRootNode.ChildList.ToArray()); + + foreach (var child in adhocRootNode.ChildList) + { + child.IsExpandable = false; + } + + var activeNode = adhocRootNode.ChildList.FirstOrDefault(); + + if (!TreeViewService.TryGetTreeViewContainer(InputFileContent.TreeViewContainerKey, out var treeViewContainer)) + { + TreeViewService.RegisterTreeViewContainer(new TreeViewContainer( + InputFileContent.TreeViewContainerKey, + adhocRootNode, + activeNode is null + ? ImmutableList.Empty + : new TreeViewNoType[] { activeNode }.ToImmutableList())); + } + else + { + TreeViewService.SetRoot(InputFileContent.TreeViewContainerKey, adhocRootNode); + + TreeViewService.SetActiveNode( + InputFileContent.TreeViewContainerKey, + activeNode, + true, + false); + } + + await pseudoRootNode.LoadChildListAsync(); + + var setOpenedTreeViewModelAction = new InputFileState.SetOpenedTreeViewModelAction( + pseudoRootNode, + IdeComponentRenderers, + CommonComponentRenderers, + FileSystemProvider, + EnvironmentProvider); + + Dispatcher.Dispatch(setOpenedTreeViewModelAction); + } + + if (!string.IsNullOrWhiteSpace(projectPersonalPath) && + await FileSystemProvider.File.ExistsAsync(projectPersonalPath)) + { + var projectAbsolutePath = EnvironmentProvider.AbsolutePathFactory( + projectPersonalPath, + false); + + Dispatcher.Dispatch(new ProgramExecutionState.SetStartupProjectAbsolutePathAction( + projectAbsolutePath)); + } + } + } + public void Dispose() { DragStateWrap.StateChanged -= DragStateWrapOnStateChanged; diff --git a/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/Internals/IdeInfoDisplay.razor b/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/Internals/IdeInfoDisplay.razor index 69d8231cb..a21ebfd70 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/Internals/IdeInfoDisplay.razor +++ b/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/Internals/IdeInfoDisplay.razor @@ -18,6 +18,49 @@
Recent Changes:
+
+
v 0.8.3.0 (2024-05-05)
+ +
    +
  • + Fix Linux solution explorer paths. (issue was relating to '\' vs '/' directory separator). +
  • +
  • + Fix Linux copy and paste. +
  • +
  • + Fix get local storage. +
  • +
  • + Fix unit test output. (still isn't perfect) +
  • +
  • + Fix insertion of text when text-editor-cursor has a selection +
  • +
  • + Fix text editor mouse wheel scrolling +
  • +
  • + Fix text editor bring cursor into view. +
  • +
  • + Batch terminal output. (this is a massive performance improvement, + instead of writing line by line, if many lines of output need to be written, + they all are written in one operation.) +
  • +
  • + Parse the output of the terminal on a 'by-command-basis'. + One can run 'dotnet run' with a .net cli output parser, + then run 'git status' with a git cli output parser, as an example. +
  • +
  • + Git integration progress. For example, a button that runs "git status", + parses the terminal output, and creates a tree view of all the changed files. + The git integration is still in progress though. (more usage of the '.git' + folder instead of just parsing the terminal so much is likely a path to take). +
  • +
+
v 0.8.2.0 (2024-04-27)
diff --git a/Source/Lib/Ide/Ide.RazorLib/StartupControls/Displays/StartupControlsDisplay.razor.cs b/Source/Lib/Ide/Ide.RazorLib/StartupControls/Displays/StartupControlsDisplay.razor.cs index 709541304..90376bed4 100644 --- a/Source/Lib/Ide/Ide.RazorLib/StartupControls/Displays/StartupControlsDisplay.razor.cs +++ b/Source/Lib/Ide/Ide.RazorLib/StartupControls/Displays/StartupControlsDisplay.razor.cs @@ -40,7 +40,8 @@ public partial class StartupControlsDisplay : FluxorComponent _newDotNetSolutionTerminalCommandKey, formattedCommand, ancestorDirectory.Value, - _newDotNetSolutionCancellationTokenSource.Token); + _newDotNetSolutionCancellationTokenSource.Token, + OutputParser: new DotNetRunOutputParser()); } private async Task StartProgramWithoutDebuggingOnClick() diff --git a/Source/Lib/Ide/Ide.RazorLib/Terminals/Displays/TerminalGroupDisplay.razor b/Source/Lib/Ide/Ide.RazorLib/Terminals/Displays/TerminalGroupDisplay.razor index 76424b048..f5f05297c 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Terminals/Displays/TerminalGroupDisplay.razor +++ b/Source/Lib/Ide/Ide.RazorLib/Terminals/Displays/TerminalGroupDisplay.razor @@ -62,12 +62,6 @@ @terminal.DisplayName - - @{ var isKillProcessDisabled = !terminal.HasExecutingProcess; }