Browse Source

hot reload wip

Krzysztof Krysiński 1 year ago
parent
commit
6637035336

+ 8 - 8
src/PixiEditor.DevTools/CsharpCoding/ProjectCompiler.cs

@@ -30,10 +30,10 @@ public class ProjectCompiler
     }
     }
 
 
     [MethodImpl(MethodImplOptions.NoInlining)]
     [MethodImpl(MethodImplOptions.NoInlining)]
-    public async Task<Assembly?> Compile(bool restore = false)
+    public Assembly Compile(bool restore = false)
     {
     {
-        //return await Compile(await GetDocuments());
-        CompiledAssembly = await CliCompile(restore);
+        CompiledAssembly = Compile(GetDocuments());
+        //CompiledAssembly = await CliCompile(restore);
         if (CompiledAssembly != null)
         if (CompiledAssembly != null)
         {
         {
             LayoutElementTypes = CompiledAssembly.GetTypes().Where(x => typeof(LayoutElement).IsAssignableFrom(x))
             LayoutElementTypes = CompiledAssembly.GetTypes().Where(x => typeof(LayoutElement).IsAssignableFrom(x))
@@ -94,13 +94,13 @@ public class ProjectCompiler
         process.WaitForExit();
         process.WaitForExit();
     }
     }
 
 
-    private async Task<HashSet<Document>> GetDocuments()
+    private HashSet<Document> GetDocuments()
     {
     {
         HashSet<Document> documents = new HashSet<Document>();
         HashSet<Document> documents = new HashSet<Document>();
         for (var i = 0; i < CsProjects.Count; i++)
         for (var i = 0; i < CsProjects.Count; i++)
         {
         {
             var project = CsProjects[i];
             var project = CsProjects[i];
-            var generated = await project.GetSourceGeneratedDocumentsAsync();
+            var generated = project.GetSourceGeneratedDocumentsAsync().Result;
             List<Document> allDocs = new List<Document>(project.Documents.Concat(generated));
             List<Document> allDocs = new List<Document>(project.Documents.Concat(generated));
 
 
             foreach (var document in allDocs)
             foreach (var document in allDocs)
@@ -120,13 +120,13 @@ public class ProjectCompiler
         return documentFilePath.EndsWith("AssemblyInfo.cs") || documentFilePath.EndsWith("AssemblyAttributes.cs");
         return documentFilePath.EndsWith("AssemblyInfo.cs") || documentFilePath.EndsWith("AssemblyAttributes.cs");
     }
     }
 
 
-    public async Task<Assembly?> Compile(HashSet<Document> documents)
+    public Assembly? Compile(HashSet<Document> documents)
     {
     {
-        await ParseSyntaxTrees(documents);
+        ParseSyntaxTrees(documents);
 
 
         var references = CreateReferences();
         var references = CreateReferences();
 
 
-        await CreateCompilation(documents, references);
+        CreateCompilation(documents, references);
         List<ResourceDescription> manifestResources = GetManifestResources();
         List<ResourceDescription> manifestResources = GetManifestResources();
 
 
         using var ms = new MemoryStream();
         using var ms = new MemoryStream();

+ 7 - 6
src/PixiEditor.DevTools/CsharpCoding/ProjectLoader.cs

@@ -26,8 +26,8 @@ public class ProjectLoader
         Workspace = MSBuildWorkspace.Create(props);
         Workspace = MSBuildWorkspace.Create(props);
         Workspace.LoadMetadataForReferencedProjects = true;
         Workspace.LoadMetadataForReferencedProjects = true;
 
 
-        PackageReferences = LoadPackageReferences(projectPath, out List<string> projects);
-        ReferencedProjectPaths = projects;
+        /*PackageReferences = LoadPackageReferences(projectPath, out List<string> projects);
+        ReferencedProjectPaths = projects;*/
         ProjectPath = projectPath;
         ProjectPath = projectPath;
     }
     }
 
 
@@ -82,17 +82,18 @@ public class ProjectLoader
             .ToList();
             .ToList();
     }
     }
 
 
-    public async Task LoadProjectsAsync()
+    public void LoadProjects()
     {
     {
         AllProjects = new List<Project>();
         AllProjects = new List<Project>();
-        foreach (var projectPath in ReferencedProjectPaths)
+        /*foreach (var projectPath in ReferencedProjectPaths)
         {
         {
             if(IsAnimacoCoreProject(projectPath))
             if(IsAnimacoCoreProject(projectPath))
                 continue;
                 continue;
             AllProjects.Add(await Workspace.OpenProjectAsync(projectPath));
             AllProjects.Add(await Workspace.OpenProjectAsync(projectPath));
-        }
+        }*/
 
 
-        TargetProject = AllProjects.First(x => x.FilePath == ProjectPath);
+        AllProjects.Add(Workspace.OpenProjectAsync(ProjectPath).Result);
+        TargetProject = AllProjects[0];
     }
     }
 
 
     private bool IsAnimacoCoreProject(string projectPath)
     private bool IsAnimacoCoreProject(string projectPath)

+ 16 - 1
src/PixiEditor.DevTools/HotReloader.cs

@@ -18,6 +18,21 @@ public class HotReloader
 
 
     private void WatcherOnChanged(object sender, FileSystemEventArgs e)
     private void WatcherOnChanged(object sender, FileSystemEventArgs e)
     {
     {
-        OnFileChanged?.Invoke(e.FullPath);
+        try
+        {
+            (sender as FileSystemWatcher).EnableRaisingEvents = false;
+            OnFileChanged?.Invoke(e.FullPath);
+        }
+
+        finally
+        {
+            (sender as FileSystemWatcher).EnableRaisingEvents = true;
+        }
+    }
+
+    public void WatchProject(string selectedProjectFile)
+    {
+        string directory = Path.GetDirectoryName(selectedProjectFile);
+        WatchFile(directory, "*.cs");
     }
     }
 }
 }

+ 44 - 8
src/PixiEditor.DevTools/Layouts/LivePreviewWindowState.cs

@@ -1,4 +1,6 @@
-using PixiEditor.Extensions.CommonApi.LayoutBuilding.Events;
+using Microsoft.CodeAnalysis.MSBuild;
+using PixiEditor.DevTools.CsharpCoding;
+using PixiEditor.Extensions.CommonApi.LayoutBuilding.Events;
 using PixiEditor.Extensions.IO;
 using PixiEditor.Extensions.IO;
 using PixiEditor.Extensions.LayoutBuilding.Elements;
 using PixiEditor.Extensions.LayoutBuilding.Elements;
 using PixiEditor.Extensions.Runtime;
 using PixiEditor.Extensions.Runtime;
@@ -8,11 +10,18 @@ namespace PixiEditor.DevTools.Layouts;
 public class LivePreviewWindowState : State
 public class LivePreviewWindowState : State
 {
 {
     private ExtensionLoader Loader { get; }
     private ExtensionLoader Loader { get; }
-    public string? SelectedLayoutFile { get; set; }
+    private ProjectLoader projectLoader;
+    private ProjectCompiler compiler;
+    private HotReloader reloader;
+
+    private LayoutElement? _element;
+    public string? SelectedProjectFile { get; set; }
 
 
     public LivePreviewWindowState()
     public LivePreviewWindowState()
     {
     {
         Loader = new ExtensionLoader(AppDomain.CurrentDomain.BaseDirectory);
         Loader = new ExtensionLoader(AppDomain.CurrentDomain.BaseDirectory);
+        reloader = new HotReloader();
+        reloader.OnFileChanged += OnFileChanged;
     }
     }
 
 
     public override LayoutElement BuildElement()
     public override LayoutElement BuildElement()
@@ -20,22 +29,49 @@ public class LivePreviewWindowState : State
         return new Align(
         return new Align(
             alignment: Alignment.TopLeft,
             alignment: Alignment.TopLeft,
             child: new Column(
             child: new Column(
-                children: new Button(
-                    child: SelectedLayoutFile != null ? new Text($"Selected extension: {SelectedLayoutFile}") : new Text("Select extension"),
-                    onClick: OnClick)
+                new Button(
+                    child: SelectedProjectFile != null ? new Text($"Selected extension: {SelectedProjectFile}") : new Text("Select extension"),
+                    onClick: OnClick),
+                _element ?? new Text("No layout element selected")
                 )
                 )
             );
             );
     }
     }
 
 
+    private void OnFileChanged(string obj)
+    {
+        compiler.Compile();
+        SetState(BuildLayoutElement);
+    }
+
+    private void BuildLayoutElement()
+    {
+        var typeToInit = compiler.LayoutElementTypes.FirstOrDefault();
+        if (typeToInit != null)
+        {
+            var instance = (LayoutElement)Activator.CreateInstance(typeToInit);
+            _element = instance;
+        }
+    }
+
     private void OnClick(ElementEventArgs args)
     private void OnClick(ElementEventArgs args)
     {
     {
-        if (DevToolsExtension.PixiEditorApi.FileSystem.OpenFileDialog(new FileFilter().AddFilter("Layout C# Script", "*.cs"), out string? path))
+        if (DevToolsExtension.PixiEditorApi.FileSystem.OpenFileDialog(new FileFilter().AddFilter("C# project file", "*.csproj"), out string? path))
         {
         {
             SetState(() =>
             SetState(() =>
             {
             {
-                SelectedLayoutFile = path;
-
+                SelectedProjectFile = path;
+                InitProject();
             });
             });
         }
         }
     }
     }
+
+    private void InitProject()
+    {
+        projectLoader = new ProjectLoader(SelectedProjectFile);
+        projectLoader.LoadProjects();
+        compiler = new ProjectCompiler(projectLoader.Workspace, projectLoader.AllProjects);
+        reloader.WatchProject(SelectedProjectFile);
+        compiler.Compile();
+        BuildLayoutElement();
+    }
 }
 }

+ 2 - 0
src/PixiEditor.DevTools/PixiEditor.DevTools.csproj

@@ -6,6 +6,7 @@
         <Nullable>enable</Nullable>
         <Nullable>enable</Nullable>
        <OutputPath>..\PixiEditor.AvaloniaUI.Desktop\bin\Debug\net8.0\Extensions\DevTools</OutputPath>
        <OutputPath>..\PixiEditor.AvaloniaUI.Desktop\bin\Debug\net8.0\Extensions\DevTools</OutputPath>
        <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
        <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
+      <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
     </PropertyGroup>
     </PropertyGroup>
 
 
     <ItemGroup>
     <ItemGroup>
@@ -23,6 +24,7 @@
     <ItemGroup>
     <ItemGroup>
       <PackageReference Include="Microsoft.Build.Locator" Version="1.6.10" />
       <PackageReference Include="Microsoft.Build.Locator" Version="1.6.10" />
       <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
       <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
+      <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0" />
       <PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.8.0" />
       <PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.8.0" />
     </ItemGroup>
     </ItemGroup>
 
 

+ 29 - 1
src/PixiEditor.Extensions/LayoutBuilding/Elements/Column.cs

@@ -1,10 +1,15 @@
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.ComponentModel;
 using Avalonia.Controls;
 using Avalonia.Controls;
+using Avalonia.Threading;
 
 
 namespace PixiEditor.Extensions.LayoutBuilding.Elements;
 namespace PixiEditor.Extensions.LayoutBuilding.Elements;
 
 
 public class Column : MultiChildLayoutElement
 public class Column : MultiChildLayoutElement
 {
 {
+    private StackPanel panel;
+
     public Column()
     public Column()
     {
     {
     }
     }
@@ -12,11 +17,34 @@ public class Column : MultiChildLayoutElement
     public Column(params LayoutElement[] children)
     public Column(params LayoutElement[] children)
     {
     {
         Children = new ObservableCollection<LayoutElement>(children);
         Children = new ObservableCollection<LayoutElement>(children);
+        Children.CollectionChanged += ChildrenOnCollectionChanged;
+    }
+
+    private void ChildrenOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
+    {
+        Dispatcher.UIThread.Invoke(() =>
+        {
+            if (e.Action == NotifyCollectionChangedAction.Add)
+            {
+                foreach (LayoutElement? item in e.NewItems)
+                {
+                    var newChild = item.BuildNative();
+                    panel.Children.Add(newChild);
+                }
+            }
+            else if (e.Action == NotifyCollectionChangedAction.Remove)
+            {
+                foreach (LayoutElement? item in e.OldItems)
+                {
+                    panel.Children.RemoveAt(e.OldStartingIndex);
+                }
+            }
+        });
     }
     }
 
 
     public override Control BuildNative()
     public override Control BuildNative()
     {
     {
-        StackPanel panel = new StackPanel
+        panel = new StackPanel
         {
         {
             Orientation = Avalonia.Layout.Orientation.Vertical
             Orientation = Avalonia.Layout.Orientation.Vertical
         };
         };

+ 1 - 2
src/PixiEditor.Extensions/LayoutBuilding/Elements/MultiChildLayoutElement.cs

@@ -20,8 +20,7 @@ public abstract class MultiChildLayoutElement : LayoutElement, IMultiChildLayout
         get => children;
         get => children;
         set
         set
         {
         {
-            children = value;
-            OnPropertyChanged();
+            SetField(ref children, value);
         }
         }
     }
     }
 
 

+ 3 - 2
src/PixiEditor.Extensions/LayoutBuilding/Elements/StatefulElement.cs

@@ -48,7 +48,7 @@ public abstract class StatefulElement<TState> : LayoutElement, IStatefulElement<
         PerformDiff(_content, newTree);
         PerformDiff(_content, newTree);
     }
     }
 
 
-    private void PerformDiff(ILayoutElement<Control> oldNode, ILayoutElement<Control> newNode)
+    private void PerformDiff(ILayoutElement<Control> oldNode, ILayoutElement<Control> newNode, IChildHost? parent = null)
     {
     {
         // Check if the node types are the same
         // Check if the node types are the same
         bool isSameType = oldNode.GetType() == newNode.GetType();
         bool isSameType = oldNode.GetType() == newNode.GetType();
@@ -60,6 +60,7 @@ public abstract class StatefulElement<TState> : LayoutElement, IStatefulElement<
         else
         else
         {
         {
             // Replace the entire node if the types are different
             // Replace the entire node if the types are different
+            parent?.RemoveChild(oldNode);
             oldNode = newNode;
             oldNode = newNode;
             return;
             return;
         }
         }
@@ -73,7 +74,7 @@ public abstract class StatefulElement<TState> : LayoutElement, IStatefulElement<
 
 
             while (oldChildren.MoveNext() && newChildren.MoveNext())
             while (oldChildren.MoveNext() && newChildren.MoveNext())
             {
             {
-                PerformDiff(oldChildren.Current, newChildren.Current);
+                PerformDiff(oldChildren.Current, newChildren.Current, oldDeserializable);
             }
             }
 
 
             if (oldChildren.Current == null && newChildren.Current != null)
             if (oldChildren.Current == null && newChildren.Current != null)