From 39a8fa3d3e0cb67aa65913e49a7a147e22b46773 Mon Sep 17 00:00:00 2001 From: glopesdev Date: Tue, 6 Aug 2024 11:38:10 +0100 Subject: [PATCH 1/5] Refactor base directory operations to helper class --- Bonsai.Editor/EditorForm.cs | 22 +++++----------------- Bonsai.Editor/Project.cs | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 17 deletions(-) create mode 100644 Bonsai.Editor/Project.cs diff --git a/Bonsai.Editor/EditorForm.cs b/Bonsai.Editor/EditorForm.cs index 48f0799a..2c01d575 100644 --- a/Bonsai.Editor/EditorForm.cs +++ b/Bonsai.Editor/EditorForm.cs @@ -31,7 +31,6 @@ public partial class EditorForm : Form { const float DefaultEditorScale = 1.0f; const string EditorUid = "editor"; - const string BonsaiExtension = ".bonsai"; const string BonsaiPackageName = "Bonsai"; const string ExtensionsDirectory = "Extensions"; const string DefinitionsDirectory = "Definitions"; @@ -288,7 +287,7 @@ protected override void OnLoad(EventArgs e) var initialFileName = FileName; var validFileName = !string.IsNullOrEmpty(initialFileName) && - Path.GetExtension(initialFileName) == BonsaiExtension && + Path.GetExtension(initialFileName) == Project.BonsaiExtension && File.Exists(initialFileName); var formClosed = Observable.FromEventPattern( @@ -298,19 +297,8 @@ protected override void OnLoad(EventArgs e) InitializeWorkflowFileWatcher().TakeUntil(formClosed).Subscribe(); updatesAvailable.TakeUntil(formClosed).ObserveOn(formScheduler).Subscribe(HandleUpdatesAvailable); - var currentDirectory = Path.GetFullPath(Environment.CurrentDirectory).TrimEnd('\\'); - var appDomainBaseDirectory = Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory).TrimEnd('\\'); - var currentDirectoryRestricted = currentDirectory == appDomainBaseDirectory; - if (!EditorSettings.IsRunningOnMono) - { - var systemPath = Path.GetFullPath(Environment.GetFolderPath(Environment.SpecialFolder.System)).TrimEnd('\\'); - var systemX86Path = Path.GetFullPath(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86)).TrimEnd('\\'); - currentDirectoryRestricted |= currentDirectory == systemPath || currentDirectory == systemX86Path; - } - - var workflowBaseDirectory = validFileName - ? Path.GetDirectoryName(initialFileName) - : (!currentDirectoryRestricted ? currentDirectory : Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)); + var currentDirectory = Project.GetCurrentBaseDirectory(out bool currentDirectoryRestricted); + var workflowBaseDirectory = validFileName ? Project.GetWorkflowBaseDirectory(initialFileName) : currentDirectory; if (currentDirectoryRestricted) { currentDirectory = workflowBaseDirectory; @@ -570,7 +558,7 @@ static IEnumerable FindWorkflows(string basePath) } else basePathLength = basePath.Length; - try { workflowFiles = Directory.GetFiles(basePath, "*" + BonsaiExtension, SearchOption.AllDirectories); } + try { workflowFiles = Directory.GetFiles(basePath, "*" + Project.BonsaiExtension, SearchOption.AllDirectories); } catch (UnauthorizedAccessException) { yield break; } catch (DirectoryNotFoundException) { yield break; } @@ -2287,7 +2275,7 @@ static bool TryGetAssemblyResource(string path, out string assemblyName, out str { const char AssemblySeparator = ':'; var separatorIndex = path.IndexOf(AssemblySeparator); - if (separatorIndex >= 0 && !Path.IsPathRooted(path) && path.EndsWith(BonsaiExtension)) + if (separatorIndex >= 0 && !Path.IsPathRooted(path) && path.EndsWith(Project.BonsaiExtension)) { path = Path.ChangeExtension(path, null); var nameElements = path.Split(new[] { AssemblySeparator }, 2); diff --git a/Bonsai.Editor/Project.cs b/Bonsai.Editor/Project.cs new file mode 100644 index 00000000..106a7f5e --- /dev/null +++ b/Bonsai.Editor/Project.cs @@ -0,0 +1,37 @@ +using System; +using System.IO; + +namespace Bonsai.Editor +{ + static class Project + { + internal const string BonsaiExtension = ".bonsai"; + + public static string GetCurrentBaseDirectory() + { + return GetCurrentBaseDirectory(out _); + } + + public static string GetCurrentBaseDirectory(out bool currentDirectoryRestricted) + { + var currentDirectory = Path.GetFullPath(Environment.CurrentDirectory).TrimEnd('\\'); + var appDomainBaseDirectory = Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory).TrimEnd('\\'); + currentDirectoryRestricted = currentDirectory == appDomainBaseDirectory; + if (!EditorSettings.IsRunningOnMono) + { + var systemPath = Path.GetFullPath(Environment.GetFolderPath(Environment.SpecialFolder.System)).TrimEnd('\\'); + var systemX86Path = Path.GetFullPath(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86)).TrimEnd('\\'); + currentDirectoryRestricted |= currentDirectory == systemPath || currentDirectory == systemX86Path; + } + + return !currentDirectoryRestricted + ? currentDirectory + : Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + } + + public static string GetWorkflowBaseDirectory(string fileName) + { + return Path.GetDirectoryName(fileName); + } + } +} From 76ba898c587a4bee4f92ba589c62a9957469d883 Mon Sep 17 00:00:00 2001 From: glopesdev Date: Tue, 6 Aug 2024 11:40:31 +0100 Subject: [PATCH 2/5] Move layout files to workflow settings folder --- Bonsai.Editor/EditorForm.cs | 15 +++++++++++++-- Bonsai.Editor/Layout/LayoutHelper.cs | 7 +++++-- Bonsai.Editor/Project.cs | 17 +++++++++++++++++ Bonsai.Editor/WorkflowRunner.cs | 2 ++ 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/Bonsai.Editor/EditorForm.cs b/Bonsai.Editor/EditorForm.cs index 2c01d575..dcfe3338 100644 --- a/Bonsai.Editor/EditorForm.cs +++ b/Bonsai.Editor/EditorForm.cs @@ -841,6 +841,7 @@ bool OpenWorkflow(string fileName, bool setWorkingDirectory) editorControl.Workflow = workflowBuilder.Workflow; editorSite.ValidateWorkflow(); +#pragma warning disable CS0612 // Support for deprecated layout config files var layoutPath = LayoutHelper.GetLayoutPath(fileName); if (File.Exists(layoutPath)) { @@ -850,6 +851,7 @@ bool OpenWorkflow(string fileName, bool setWorkingDirectory) catch (InvalidOperationException) { } } } +#pragma warning restore CS0612 // Support for deprecated layout config files saveWorkflowDialog.FileName = fileName; ResetProjectStatus(); @@ -903,8 +905,17 @@ bool SaveWorkflow(string fileName) editorControl.UpdateVisualizerLayout(); if (editorControl.VisualizerLayout != null) { - var layoutPath = LayoutHelper.GetLayoutPath(fileName); - SaveVisualizerLayout(layoutPath, editorControl.VisualizerLayout); + var layoutPath = new FileInfo(Project.GetLayoutConfigPath(fileName)); + layoutPath.Directory?.Create(); + + SaveVisualizerLayout(layoutPath.FullName, editorControl.VisualizerLayout); +#pragma warning disable CS0612 // Support for deprecated layout config files + var legacyLayoutPath = new FileInfo(Project.GetLegacyLayoutConfigPath(fileName)); + if (legacyLayoutPath.Exists) + { + legacyLayoutPath.Delete(); + } +#pragma warning restore CS0612 // Support for deprecated layout config files } UpdateWorkflowDirectory(fileName); diff --git a/Bonsai.Editor/Layout/LayoutHelper.cs b/Bonsai.Editor/Layout/LayoutHelper.cs index 3047766f..5e1f7491 100644 --- a/Bonsai.Editor/Layout/LayoutHelper.cs +++ b/Bonsai.Editor/Layout/LayoutHelper.cs @@ -12,7 +12,6 @@ namespace Bonsai.Design { static class LayoutHelper { - const string LayoutExtension = ".layout"; static readonly XName XsdAttributeName = ((XNamespace)"http://www.w3.org/2000/xmlns/") + "xsd"; static readonly XName XsiAttributeName = ((XNamespace)"http://www.w3.org/2000/xmlns/") + "xsi"; const string XsdAttributeValue = "http://www.w3.org/2001/XMLSchema"; @@ -25,9 +24,13 @@ public static VisualizerDialogSettings GetLayoutSettings(this VisualizerLayout v return visualizerLayout?.DialogSettings.FirstOrDefault(xs => xs.Tag == key || xs.Tag == null); } + [Obsolete] public static string GetLayoutPath(string fileName) { - return Path.ChangeExtension(fileName, Path.GetExtension(fileName) + LayoutExtension); + var newLayoutPath = Editor.Project.GetLayoutConfigPath(fileName); + return File.Exists(newLayoutPath) + ? newLayoutPath + : Editor.Project.GetLegacyLayoutConfigPath(fileName); } public static void SetLayoutTags(ExpressionBuilderGraph source, VisualizerLayout layout) diff --git a/Bonsai.Editor/Project.cs b/Bonsai.Editor/Project.cs index 106a7f5e..1be66e17 100644 --- a/Bonsai.Editor/Project.cs +++ b/Bonsai.Editor/Project.cs @@ -6,6 +6,7 @@ namespace Bonsai.Editor static class Project { internal const string BonsaiExtension = ".bonsai"; + internal const string LayoutExtension = ".layout"; public static string GetCurrentBaseDirectory() { @@ -33,5 +34,21 @@ public static string GetWorkflowBaseDirectory(string fileName) { return Path.GetDirectoryName(fileName); } + + public static string GetWorkflowSettingsDirectory(string fileName) + { + return Path.Combine(BonsaiExtension, Path.GetFileNameWithoutExtension(fileName)); + } + + public static string GetLayoutConfigPath(string fileName) + { + return Path.Combine(GetWorkflowSettingsDirectory(fileName), LayoutExtension); + } + + [Obsolete] + internal static string GetLegacyLayoutConfigPath(string fileName) + { + return Path.ChangeExtension(fileName, Path.GetExtension(fileName) + LayoutExtension); + } } } diff --git a/Bonsai.Editor/WorkflowRunner.cs b/Bonsai.Editor/WorkflowRunner.cs index e3ed7e0a..adeeae33 100644 --- a/Bonsai.Editor/WorkflowRunner.cs +++ b/Bonsai.Editor/WorkflowRunner.cs @@ -137,7 +137,9 @@ public static void Run( } var workflowBuilder = ElementStore.LoadWorkflow(fileName); +#pragma warning disable CS0612 // Support for deprecated layout config files layoutPath ??= LayoutHelper.GetLayoutPath(fileName); +#pragma warning restore CS0612 // Support for deprecated layout config files if (visualizerProvider != null && File.Exists(layoutPath)) { VisualizerLayout layout = null; From 6c308936c9d4312362b0481e78cecb5d91d86085 Mon Sep 17 00:00:00 2001 From: glopesdev Date: Tue, 6 Aug 2024 11:58:38 +0100 Subject: [PATCH 3/5] Refactor extension workflow search to helper class --- Bonsai.Editor/EditorForm.cs | 59 ++----------------------------------- Bonsai.Editor/Project.cs | 45 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 56 deletions(-) diff --git a/Bonsai.Editor/EditorForm.cs b/Bonsai.Editor/EditorForm.cs index dcfe3338..e8af45f2 100644 --- a/Bonsai.Editor/EditorForm.cs +++ b/Bonsai.Editor/EditorForm.cs @@ -32,11 +32,9 @@ public partial class EditorForm : Form const float DefaultEditorScale = 1.0f; const string EditorUid = "editor"; const string BonsaiPackageName = "Bonsai"; - const string ExtensionsDirectory = "Extensions"; const string DefinitionsDirectory = "Definitions"; const string WorkflowCategoryName = "Workflow"; const string SubjectCategoryName = "Subject"; - const string DefaultWorkflowNamespace = "Unspecified"; static readonly AttributeCollection DesignTimeAttributes = new AttributeCollection(BrowsableAttribute.Yes, DesignTimeVisibleAttribute.Yes); static readonly AttributeCollection RuntimeAttributes = AttributeCollection.FromExisting(DesignTimeAttributes, DesignOnlyAttribute.No); static readonly char[] ToolboxArgumentSeparator = new[] { ' ' }; @@ -307,7 +305,7 @@ protected override void OnLoad(EventArgs e) directoryToolStripItem.Text = currentDirectory; openWorkflowDialog.InitialDirectory = saveWorkflowDialog.InitialDirectory = currentDirectory; - extensionsPath = new DirectoryInfo(Path.Combine(workflowBaseDirectory, ExtensionsDirectory)); + extensionsPath = new DirectoryInfo(Path.Combine(workflowBaseDirectory, Project.ExtensionsDirectory)); if (extensionsPath.Exists) OnExtensionsDirectoryChanged(EventArgs.Empty); InitializeEditorToolboxTypes(); @@ -506,7 +504,7 @@ IObservable RefreshWorkflowExtensions() .Merge(start, changed, created, deleted, renamed) .Throttle(TimeSpan.FromSeconds(1), Scheduler.Default) .Select(evt => workflowExtensions - .Concat(FindWorkflows(ExtensionsDirectory)) + .Concat(Project.EnumerateExtensionWorkflows(extensionsPath.FullName)) .GroupBy(x => x.Namespace) .ToList()) .ObserveOn(formScheduler) @@ -546,57 +544,6 @@ IObservable RefreshWorkflowExtensions() .Select(xs => Unit.Default); } - static IEnumerable FindWorkflows(string basePath) - { - int basePathLength; - string[] workflowFiles; - if (!Path.IsPathRooted(basePath)) - { - var currentDirectory = Environment.CurrentDirectory; - basePath = Path.Combine(currentDirectory, basePath); - basePathLength = currentDirectory.Length; - } - else basePathLength = basePath.Length; - - try { workflowFiles = Directory.GetFiles(basePath, "*" + Project.BonsaiExtension, SearchOption.AllDirectories); } - catch (UnauthorizedAccessException) { yield break; } - catch (DirectoryNotFoundException) { yield break; } - - foreach (var fileName in workflowFiles) - { - var description = string.Empty; - try - { - using (var reader = XmlReader.Create(fileName, new XmlReaderSettings { IgnoreWhitespace = true })) - { - reader.ReadStartElement(typeof(WorkflowBuilder).Name); - if (reader.Name == nameof(WorkflowBuilder.Description)) - { - reader.ReadStartElement(); - description = reader.Value; - } - } - } - catch (SystemException) { continue; } - - var relativePath = fileName.Substring(basePathLength) - .TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - var fileNamespace = Path.GetDirectoryName(relativePath) - .Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) - .Replace(Path.DirectorySeparatorChar, ExpressionHelper.MemberSeparator.First()); - if (string.IsNullOrEmpty(fileNamespace)) fileNamespace = DefaultWorkflowNamespace; - - yield return new WorkflowElementDescriptor - { - Name = Path.GetFileNameWithoutExtension(relativePath), - Namespace = fileNamespace, - FullyQualifiedName = relativePath, - Description = description, - ElementTypes = new[] { ~ElementCategory.Workflow } - }; - } - } - IObservable InitializeTypeVisualizers() { var visualizerMapping = from typeVisualizer in visualizerElements @@ -643,7 +590,7 @@ IEnumerable InitializeWorkflowExtensions(IEnumerable< static string GetPackageDisplayName(string packageKey) { - if (packageKey == null) return ExtensionsDirectory; + if (packageKey == null) return Project.ExtensionsDirectory; if (packageKey == BonsaiPackageName) return packageKey; return packageKey.Replace(BonsaiPackageName + ".", string.Empty); } diff --git a/Bonsai.Editor/Project.cs b/Bonsai.Editor/Project.cs index 1be66e17..2d82944c 100644 --- a/Bonsai.Editor/Project.cs +++ b/Bonsai.Editor/Project.cs @@ -1,12 +1,18 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Xml; +using Bonsai.Design; namespace Bonsai.Editor { static class Project { + const string DefaultWorkflowNamespace = "Unspecified"; internal const string BonsaiExtension = ".bonsai"; internal const string LayoutExtension = ".layout"; + internal const string ExtensionsDirectory = "Extensions"; public static string GetCurrentBaseDirectory() { @@ -50,5 +56,44 @@ internal static string GetLegacyLayoutConfigPath(string fileName) { return Path.ChangeExtension(fileName, Path.GetExtension(fileName) + LayoutExtension); } + + public static IEnumerable EnumerateExtensionWorkflows(string basePath) + { + IEnumerable workflowFiles; + try { workflowFiles = Directory.EnumerateFiles(basePath, "*" + BonsaiExtension, SearchOption.AllDirectories); } + catch (UnauthorizedAccessException) { yield break; } + catch (DirectoryNotFoundException) { yield break; } + + foreach (var fileName in workflowFiles) + { + var description = string.Empty; + try + { + using var reader = XmlReader.Create(fileName, new XmlReaderSettings { IgnoreWhitespace = true }); + reader.ReadStartElement(typeof(WorkflowBuilder).Name); + if (reader.Name == nameof(WorkflowBuilder.Description)) + { + reader.ReadStartElement(); + description = reader.Value; + } + } + catch (SystemException) { continue; } + + var relativePath = PathConvert.GetProjectPath(fileName); + var fileNamespace = Path.GetDirectoryName(relativePath) + .Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) + .Replace(Path.DirectorySeparatorChar, ExpressionHelper.MemberSeparator.First()); + if (string.IsNullOrEmpty(fileNamespace)) fileNamespace = DefaultWorkflowNamespace; + + yield return new WorkflowElementDescriptor + { + Name = Path.GetFileNameWithoutExtension(relativePath), + Namespace = fileNamespace, + FullyQualifiedName = relativePath, + Description = description, + ElementTypes = new[] { ~ElementCategory.Workflow } + }; + } + } } } From e456ef44a52ec8094b0b957f3ebd2815ed6deac7 Mon Sep 17 00:00:00 2001 From: glopesdev Date: Tue, 6 Aug 2024 12:10:12 +0100 Subject: [PATCH 4/5] Refactor definitions temp path away from main form --- Bonsai.Editor/EditorForm.cs | 5 ++--- Bonsai.Editor/Project.cs | 6 ++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Bonsai.Editor/EditorForm.cs b/Bonsai.Editor/EditorForm.cs index e8af45f2..100f0509 100644 --- a/Bonsai.Editor/EditorForm.cs +++ b/Bonsai.Editor/EditorForm.cs @@ -32,7 +32,6 @@ public partial class EditorForm : Form const float DefaultEditorScale = 1.0f; const string EditorUid = "editor"; const string BonsaiPackageName = "Bonsai"; - const string DefinitionsDirectory = "Definitions"; const string WorkflowCategoryName = "Workflow"; const string SubjectCategoryName = "Subject"; static readonly AttributeCollection DesignTimeAttributes = new AttributeCollection(BrowsableAttribute.Yes, DesignTimeVisibleAttribute.Yes); @@ -162,7 +161,7 @@ public EditorForm( documentationProvider = (IDocumentationProvider)serviceProvider.GetService(typeof(IDocumentationProvider)); } - definitionsPath = Path.Combine(Path.GetTempPath(), DefinitionsDirectory + "." + GuidHelper.GetProcessGuid().ToString()); + definitionsPath = Project.GetDefinitionsTempPath(); editorControl = new WorkflowEditorControl(editorSite); editorControl.Enter += new EventHandler(editorControl_Enter); editorControl.Workflow = workflowBuilder.Workflow; @@ -2736,7 +2735,7 @@ public void ShowDefinition(object component) extension = provider.FileExtension; } - var directory = Directory.CreateDirectory(Path.Combine(siteForm.definitionsPath, DefinitionsDirectory)); + var directory = Directory.CreateDirectory(Path.Combine(siteForm.definitionsPath, Project.DefinitionsDirectory)); var sourceFile = Path.Combine(directory.FullName, type.FullName + "." + extension); File.WriteAllText(sourceFile, source); ScriptEditorLauncher.Launch(siteForm, siteForm.scriptEnvironment.ProjectFileName, sourceFile); diff --git a/Bonsai.Editor/Project.cs b/Bonsai.Editor/Project.cs index 2d82944c..90690d8d 100644 --- a/Bonsai.Editor/Project.cs +++ b/Bonsai.Editor/Project.cs @@ -13,6 +13,7 @@ static class Project internal const string BonsaiExtension = ".bonsai"; internal const string LayoutExtension = ".layout"; internal const string ExtensionsDirectory = "Extensions"; + internal const string DefinitionsDirectory = "Definitions"; public static string GetCurrentBaseDirectory() { @@ -57,6 +58,11 @@ internal static string GetLegacyLayoutConfigPath(string fileName) return Path.ChangeExtension(fileName, Path.GetExtension(fileName) + LayoutExtension); } + internal static string GetDefinitionsTempPath() + { + return Path.Combine(Path.GetTempPath(), DefinitionsDirectory + "." + GuidHelper.GetProcessGuid().ToString()); + } + public static IEnumerable EnumerateExtensionWorkflows(string basePath) { IEnumerable workflowFiles; From 191896600eda4210302561e8d443ab446752058b Mon Sep 17 00:00:00 2001 From: glopesdev Date: Tue, 3 Sep 2024 15:18:21 +0100 Subject: [PATCH 5/5] Remove leading dot from layout file name --- Bonsai.Editor/Project.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Bonsai.Editor/Project.cs b/Bonsai.Editor/Project.cs index 90690d8d..44624b4a 100644 --- a/Bonsai.Editor/Project.cs +++ b/Bonsai.Editor/Project.cs @@ -11,7 +11,7 @@ static class Project { const string DefaultWorkflowNamespace = "Unspecified"; internal const string BonsaiExtension = ".bonsai"; - internal const string LayoutExtension = ".layout"; + internal const string LayoutFileName = "layout"; internal const string ExtensionsDirectory = "Extensions"; internal const string DefinitionsDirectory = "Definitions"; @@ -49,13 +49,13 @@ public static string GetWorkflowSettingsDirectory(string fileName) public static string GetLayoutConfigPath(string fileName) { - return Path.Combine(GetWorkflowSettingsDirectory(fileName), LayoutExtension); + return Path.Combine(GetWorkflowSettingsDirectory(fileName), LayoutFileName); } [Obsolete] internal static string GetLegacyLayoutConfigPath(string fileName) { - return Path.ChangeExtension(fileName, Path.GetExtension(fileName) + LayoutExtension); + return Path.ChangeExtension(fileName, Path.GetExtension(fileName) + "." + LayoutFileName); } internal static string GetDefinitionsTempPath()