diff --git a/demo/OneWare.Demo/DemoApp.cs b/demo/OneWare.Demo/DemoApp.cs index d75b5b80..cc36a89d 100644 --- a/demo/OneWare.Demo/DemoApp.cs +++ b/demo/OneWare.Demo/DemoApp.cs @@ -57,13 +57,12 @@ public override void Initialize() protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { base.ConfigureModuleCatalog(moduleCatalog); + moduleCatalog.AddModule(); moduleCatalog.AddModule(); moduleCatalog.AddModule(); moduleCatalog.AddModule(); moduleCatalog.AddModule(); moduleCatalog.AddModule(); - moduleCatalog.AddModule(); - moduleCatalog.AddModule(); } } \ No newline at end of file diff --git a/src/OneWare.Core/Services/Paths.cs b/src/OneWare.Core/Services/Paths.cs index f7f2e656..1f851f93 100644 --- a/src/OneWare.Core/Services/Paths.cs +++ b/src/OneWare.Core/Services/Paths.cs @@ -39,7 +39,7 @@ public Paths(string appName, string appIconPath) Directory.CreateDirectory(CrashReportsDirectory); Directory.CreateDirectory(ProjectsDirectory); //... - + var sessionsDir = Path.Combine(TempDirectory, "OneWare", "Sessions"); CleanupSessions(sessionsDir); @@ -53,7 +53,6 @@ public Paths(string appName, string appIconPath) private static void CleanupSessions(string sessionsDir) { - //Cleanup try { if (Directory.Exists(sessionsDir)) diff --git a/src/OneWare.Core/Styles/Icons.axaml b/src/OneWare.Core/Styles/Icons.axaml index 6d7add04..521bc9ee 100644 --- a/src/OneWare.Core/Styles/Icons.axaml +++ b/src/OneWare.Core/Styles/Icons.axaml @@ -2,33 +2,40 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -111,9 +118,10 @@ - + - + @@ -124,11 +132,12 @@ Geometry="F1M11.5664,9.0039C11.2934,9.0389 11.0684,9.0889 10.8924,9.1529 10.7164,9.2179 10.5804,9.2969 10.4824,9.3909 10.3844,9.4839 10.3174,9.5959 10.2804,9.7249 10.2434,9.8539 10.2244,9.9999 10.2244,10.1639 10.2244,10.3049 10.2504,10.4359 10.3004,10.5569 10.3514,10.6779 10.4244,10.7829 10.5204,10.8729 10.6164,10.9629 10.7354,11.0329 10.8784,11.0839 11.0204,11.1349 11.1854,11.1599 11.3734,11.1599 11.6154,11.1599 11.8374,11.1149 12.0414,11.0249 12.2444,10.9359 12.4194,10.8119 12.5654,10.6529 12.7124,10.4949 12.8264,10.3099 12.9084,10.0969 12.9904,9.8839 13.0314,9.6539 13.0314,9.4079L13.0314,8.7989z M13.0194,11.8339L13.0194,10.8969 12.9964,10.8969C12.7964,11.2479 12.5404,11.5169 12.2254,11.7019 11.9114,11.8879 11.5524,11.9809 11.1504,11.9809 10.8374,11.9809 10.5624,11.9359 10.3244,11.8459 10.0864,11.7559 9.8864,11.6339 9.7264,11.4799 9.5664,11.3249 9.4454,11.1419 9.3634,10.9289 9.2814,10.7159 9.2404,10.4859 9.2404,10.2399 9.2404,9.9789 9.2794,9.7379 9.3574,9.5199 9.4354,9.3009 9.5554,9.1079 9.7174,8.9399 9.8794,8.7719 10.0854,8.6319 10.3334,8.5209 10.5814,8.4089 10.8774,8.3299 11.2204,8.2829L13.0314,8.0309C13.0314,7.7579 13.0024,7.5249 12.9434,7.3309 12.8844,7.1379 12.8004,6.9809 12.6914,6.8589 12.5824,6.7379 12.4504,6.6499 12.2954,6.5929 12.1414,6.5359 11.9704,6.5079 11.7834,6.5079 11.4204,6.5079 11.0694,6.5719 10.7314,6.7009 10.3934,6.8299 10.0914,7.0099 9.8264,7.2399L9.8264,6.2559C9.9084,6.1969 10.0224,6.1349 10.1694,6.0679 10.3154,6.0019 10.4804,5.9409 10.6644,5.8839 10.8474,5.8269 11.0424,5.7799 11.2474,5.7429 11.4524,5.7059 11.6544,5.6879 11.8534,5.6879 12.5524,5.6879 13.0824,5.8819 13.4414,6.2709 13.8004,6.6589 13.9804,7.2129 13.9804,7.9319L13.9804,11.8339z M4.9454,4.4509L3.4804,8.5939 6.3984,8.5939C6.3984,8.5939,4.9534,4.4859,4.9454,4.4509 M7.6114,11.8339L6.7204,9.4839 3.1584,9.4839 2.3204,11.8339 1.2244,11.8339 4.4414,3.4319 5.4614,3.4319 8.7014,11.8339z" /> - + - + - + @@ -156,7 +165,8 @@ - + @@ -289,10 +299,13 @@ - - - - + + + + @@ -630,11 +643,12 @@ - + - + - + @@ -952,6 +966,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/OneWare.ProjectSystem/Models/ProjectEntry.cs b/src/OneWare.ProjectSystem/Models/ProjectEntry.cs index 3adb52ed..d32da5df 100644 --- a/src/OneWare.ProjectSystem/Models/ProjectEntry.cs +++ b/src/OneWare.ProjectSystem/Models/ProjectEntry.cs @@ -39,6 +39,8 @@ public IImage? Icon get => _icon; set => SetProperty(ref _icon, value); } + + public ObservableCollection IconOverlays { get; } = new(); private string _header; public string Header diff --git a/src/OneWare.ProjectSystem/Models/ProjectRoot.cs b/src/OneWare.ProjectSystem/Models/ProjectRoot.cs index cda2f533..68e972b8 100644 --- a/src/OneWare.ProjectSystem/Models/ProjectRoot.cs +++ b/src/OneWare.ProjectSystem/Models/ProjectRoot.cs @@ -28,12 +28,12 @@ protected ProjectRoot(string rootFolderPath) : base(Path.GetFileName(rootFolderP TopFolder = this; } - internal void RegisterEntry(IProjectEntry entry) + public virtual void RegisterEntry(IProjectEntry entry) { if(entry is ProjectFile file) Files.Add(file); } - internal void UnregisterEntry(IProjectEntry entry) + public virtual void UnregisterEntry(IProjectEntry entry) { if (entry is ProjectFile file) Files.Remove(file); } diff --git a/src/OneWare.Shared/Models/IProjectEntry.cs b/src/OneWare.Shared/Models/IProjectEntry.cs index c1d6624b..76a079e1 100644 --- a/src/OneWare.Shared/Models/IProjectEntry.cs +++ b/src/OneWare.Shared/Models/IProjectEntry.cs @@ -13,6 +13,8 @@ public interface IProjectEntry : IHasPath public IImage Icon { get; } + public ObservableCollection IconOverlays { get; } + public bool IsExpanded { get; set; } public IBrush Background { get; set; } diff --git a/src/OneWare.UniversalFpgaProjectSystem/INodeProvider.cs b/src/OneWare.UniversalFpgaProjectSystem/INodeProvider.cs new file mode 100644 index 00000000..32870ab8 --- /dev/null +++ b/src/OneWare.UniversalFpgaProjectSystem/INodeProvider.cs @@ -0,0 +1,9 @@ +using OneWare.Shared.Models; +using OneWare.UniversalFpgaProjectSystem.Models; + +namespace OneWare.UniversalFpgaProjectSystem; + +public interface INodeProvider +{ + public IEnumerable ExtractNodes(IProjectFile file); +} \ No newline at end of file diff --git a/src/OneWare.UniversalFpgaProjectSystem/Models/FpgaModelBase.cs b/src/OneWare.UniversalFpgaProjectSystem/Models/FpgaModelBase.cs index 39f63f57..d3629aad 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/Models/FpgaModelBase.cs +++ b/src/OneWare.UniversalFpgaProjectSystem/Models/FpgaModelBase.cs @@ -19,6 +19,9 @@ public abstract class FpgaModelBase : ObservableObject public Dictionary Pins { get; } = new(); public ObservableCollection VisiblePins { get; } = new(); + + public Dictionary Nodes { get; } = new(); + public ObservableCollection VisibleNodes { get; } = new(); private FpgaPinModel? _selectedPin; diff --git a/src/OneWare.UniversalFpgaProjectSystem/Models/UniversalFpgaProjectRoot.cs b/src/OneWare.UniversalFpgaProjectSystem/Models/UniversalFpgaProjectRoot.cs index 0b2bf467..c9a95694 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/Models/UniversalFpgaProjectRoot.cs +++ b/src/OneWare.UniversalFpgaProjectSystem/Models/UniversalFpgaProjectRoot.cs @@ -1,6 +1,10 @@ using System.Text.Json; using System.Text.Json.Nodes; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; using Avalonia.Media.Imaging; +using Avalonia.Styling; using OneWare.ProjectSystem.Models; using OneWare.Shared.Converters; using OneWare.Shared.Helpers; @@ -20,14 +24,46 @@ public class UniversalFpgaProjectRoot : ProjectRoot, IProjectRootWithFile public string ProjectFilePath { get; } public JsonObject Properties { get; } + private readonly IImage _topEntityOverlay; + + private IProjectEntry? _topEntity; + + public IProjectEntry? TopEntity + { + get => _topEntity; + set + { + _topEntity?.IconOverlays.Remove(_topEntityOverlay); + SetProperty(ref _topEntity, value); + _topEntity?.IconOverlays.Add(_topEntityOverlay); + + if (_topEntity != null) + Properties[nameof(TopEntity)] = _topEntity.RelativePath; + else + Properties.Remove(nameof(TopEntity)); + } + } + public UniversalFpgaProjectRoot(string projectFilePath, JsonObject properties) : base(Path.GetDirectoryName(projectFilePath) ?? throw new NullReferenceException("Invalid Project Path")) { ProjectFilePath = projectFilePath; Properties = properties; + _topEntityOverlay = Application.Current!.FindResource(ThemeVariant.Dark, "VsImageLib2019.DownloadOverlay16X") as IImage + ?? throw new NullReferenceException("TopEntity Icon"); + Icon = SharedConverters.PathToBitmapConverter.Convert(ContainerLocator.Container.Resolve().AppIconPath, typeof(Bitmap), null, null) as Bitmap; } - + + public override void UnregisterEntry(IProjectEntry entry) + { + if (entry == TopEntity) + { + TopEntity = null; + } + base.UnregisterEntry(entry); + } + public override bool IsPathIncluded(string relativePath) { var includes = Properties["Include"].Deserialize(); diff --git a/src/OneWare.UniversalFpgaProjectSystem/Services/NodeProviderService.cs b/src/OneWare.UniversalFpgaProjectSystem/Services/NodeProviderService.cs new file mode 100644 index 00000000..5f362b0e --- /dev/null +++ b/src/OneWare.UniversalFpgaProjectSystem/Services/NodeProviderService.cs @@ -0,0 +1,24 @@ +namespace OneWare.UniversalFpgaProjectSystem.Services; + +public class NodeProviderService +{ + private readonly Dictionary _providers = new(); + + public NodeProviderService() + { + Console.WriteLine("?"); + } + + public void RegisterNodeProvider(INodeProvider provider, params string[] extensions) + { + foreach (var ext in extensions) + { + _providers[ext] = provider; + } + } + + public INodeProvider? GetNodeProvider(string extension) + { + return _providers.TryGetValue(extension, out var provider) ? provider : null; + } +} \ No newline at end of file diff --git a/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectManager.cs b/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectManager.cs index f0052875..d892c813 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectManager.cs +++ b/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectManager.cs @@ -36,10 +36,18 @@ await _windowService.ShowDialogAsync(new UniversalFpgaProjectCreatorView() public async Task LoadProjectAsync(string path) { var root = await UniversalFpgaProjectParser.DeserializeAsync(path); + + if (root == null) return root; - if(root != null) - ProjectHelper.ImportEntries(root.FullPath, root); + ProjectHelper.ImportEntries(root.FullPath, root); + //Load Properties + var top = root.Properties["TopEntity"]; + if (top != null && root.Search(top.ToString()) is {} entity) + { + root.TopEntity = entity; + } + return root; } @@ -71,6 +79,30 @@ public IEnumerable ConstructContextMenu(IProjectEntry entry) Command = new AsyncRelayCommand(() => _dockService.OpenFileAsync(_projectExplorerService.GetTemporaryFile(root.ProjectFilePath))), }; break; + case IProjectFile { Root: UniversalFpgaProjectRoot universalFpgaProjectRoot } file: + if (universalFpgaProjectRoot.TopEntity == file) + { + yield return new MenuItemModel("Unset Top Entity") + { + Header = $"Unset Top Entity", + Command = new RelayCommand(() => + { + universalFpgaProjectRoot.TopEntity = null; + }), + }; + } + else + { + yield return new MenuItemModel("Set Top Entity") + { + Header = $"Set Top Entity", + Command = new RelayCommand(() => + { + universalFpgaProjectRoot.TopEntity = file; + }), + }; + } + break; } } } \ No newline at end of file diff --git a/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectSystemModule.cs b/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectSystemModule.cs index 0006d61f..cc8476aa 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectSystemModule.cs +++ b/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectSystemModule.cs @@ -22,6 +22,7 @@ public void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterSingleton(); containerRegistry.RegisterSingleton(); + containerRegistry.RegisterSingleton(); } public void OnInitialized(IContainerProvider containerProvider) diff --git a/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCompileViewModel.cs b/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCompileViewModel.cs index c8f11c20..f4480216 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCompileViewModel.cs +++ b/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCompileViewModel.cs @@ -1,6 +1,8 @@ using System.Collections.ObjectModel; +using AvaloniaEdit.Utils; using CommunityToolkit.Mvvm.ComponentModel; using OneWare.Shared.Controls; +using OneWare.Shared.Models; using OneWare.UniversalFpgaProjectSystem.Models; using OneWare.UniversalFpgaProjectSystem.Services; using OneWare.UniversalFpgaProjectSystem.Views; @@ -28,13 +30,23 @@ public bool HideExtensions set => SetProperty(ref _hideExtensions, value); } - public UniversalFpgaProjectCompileViewModel(FpgaService fpgaService, UniversalFpgaProjectRoot project) + public UniversalFpgaProjectCompileViewModel(FpgaService fpgaService, NodeProviderService nodeProviderService, UniversalFpgaProjectRoot project) { _project = project; FpgaModels = fpgaService.FpgaModels; SelectedFpga = FpgaModels.FirstOrDefault(); + + if (project.TopEntity is IProjectFile file) + { + var provider = nodeProviderService.GetNodeProvider(file.Extension); + if (provider is not null) + { + var nodes = provider.ExtractNodes(file); + SelectedFpga?.VisibleNodes.AddRange(nodes); + } + } } public async Task SaveAsync(FlexibleWindow window) diff --git a/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml b/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml index de2a186c..8d8155e8 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml +++ b/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml @@ -41,17 +41,18 @@ IsVisible="{Binding Converter={x:Static converters:SharedConverters.ViewFoundConverter}}" Content="{Binding }" /> - - + + + + - - + Name="SignalList" ItemsSource="{Binding VisibleNodes}" + SelectedItem="{Binding Path=SelectedNode, Mode=TwoWay}" VerticalAlignment="Stretch"> @@ -61,7 +62,41 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/OneWare.Vhdl/OneWare.Vhdl.csproj b/src/OneWare.Vhdl/OneWare.Vhdl.csproj index 87fb1915..1437797e 100644 --- a/src/OneWare.Vhdl/OneWare.Vhdl.csproj +++ b/src/OneWare.Vhdl/OneWare.Vhdl.csproj @@ -7,6 +7,7 @@ + diff --git a/src/OneWare.Vhdl/Parsing/VhdlNodeProvider.cs b/src/OneWare.Vhdl/Parsing/VhdlNodeProvider.cs new file mode 100644 index 00000000..f8f1978f --- /dev/null +++ b/src/OneWare.Vhdl/Parsing/VhdlNodeProvider.cs @@ -0,0 +1,45 @@ +using System.Text.RegularExpressions; +using OneWare.Shared.Models; +using OneWare.UniversalFpgaProjectSystem; +using OneWare.UniversalFpgaProjectSystem.Models; + +namespace OneWare.Vhdl.Parsing; + +public class VhdlNodeProvider : INodeProvider +{ + public IEnumerable ExtractNodes(IProjectFile file) + { + var fileLines = File.ReadAllLines(file.FullPath); + + bool inEntity = false; + foreach (var line in fileLines) + { + // Check for entity start + if (Regex.IsMatch(line, @"\bentity\b", RegexOptions.IgnoreCase)) + { + inEntity = true; + continue; + } + + // Check for entity end + if (inEntity && Regex.IsMatch(line, @"\bend entity\b", RegexOptions.IgnoreCase)) + { + inEntity = false; + continue; + } + + // Extract inputs and outputs + if (inEntity) + { + MatchCollection matches = Regex.Matches(line, @"\b(\w+)\s*:\s*(in|out)\s*\w+", RegexOptions.IgnoreCase); + foreach (Match match in matches) + { + string portName = match.Groups[1].Value; + string direction = match.Groups[2].Value; + + yield return new NodeModel(portName); + } + } + } + } +} \ No newline at end of file diff --git a/src/OneWare.Vhdl/Parsing/VhdlParser.cs b/src/OneWare.Vhdl/Parsing/VhdlParser.cs index 46a0a338..95e9045a 100644 --- a/src/OneWare.Vhdl/Parsing/VhdlParser.cs +++ b/src/OneWare.Vhdl/Parsing/VhdlParser.cs @@ -1,4 +1,6 @@ -namespace OneWare.Vhdl.Parsing; +using OneWare.UniversalFpgaProjectSystem.Models; + +namespace OneWare.Vhdl.Parsing; public class VhdlParser { diff --git a/src/OneWare.Vhdl/VhdlModule.cs b/src/OneWare.Vhdl/VhdlModule.cs index 2d0045bd..f484e933 100644 --- a/src/OneWare.Vhdl/VhdlModule.cs +++ b/src/OneWare.Vhdl/VhdlModule.cs @@ -1,4 +1,6 @@ using OneWare.Shared.Services; +using OneWare.UniversalFpgaProjectSystem.Services; +using OneWare.Vhdl.Parsing; using Prism.Ioc; using Prism.Modularity; @@ -16,5 +18,7 @@ public void OnInitialized(IContainerProvider containerProvider) containerProvider.Resolve().RegisterErrorSource("RustHDL"); containerProvider.Resolve().RegisterTextMateLanguage("vhdl", "avares://OneWare.Vhdl/Assets/vhdl.tmLanguage.json", ".vhd", ".vhdl"); containerProvider.Resolve().RegisterService(typeof(LanguageServiceVhdl),true, ".vhd", ".vhdl"); + + containerProvider.Resolve().RegisterNodeProvider(new VhdlNodeProvider(), ".vhd", ".vhdl"); } } \ No newline at end of file