From 2b86cdafa06bca9c4595f9ea0e7036ee3f60e0ce Mon Sep 17 00:00:00 2001 From: Peter Laske <37439758+laske185@users.noreply.github.com> Date: Tue, 11 Mar 2025 16:07:07 +0100 Subject: [PATCH 1/4] feat: Add support for solution filter files (.slnf) and fix version detection in launcher --- .../Stride.Core.Assets/PackageSession.cs | 7 +- .../PackageSessionHelper.Solution.cs | 68 +++++++---- .../VisualStudio/Solution.cs | 56 +++++++++ .../VisualStudio/SolutionFilter.cs | 92 +++++++++++++++ .../VisualStudio/SolutionFilterReader.cs | 109 ++++++++++++++++++ .../Services/EditorDialogHelper.cs | 3 +- .../ViewModel/EditorViewModel.cs | 1 + 7 files changed, 310 insertions(+), 26 deletions(-) create mode 100644 sources/core/Stride.Core.Design/VisualStudio/SolutionFilter.cs create mode 100644 sources/core/Stride.Core.Design/VisualStudio/SolutionFilterReader.cs diff --git a/sources/assets/Stride.Core.Assets/PackageSession.cs b/sources/assets/Stride.Core.Assets/PackageSession.cs index c0f05c915d..5c3199a59d 100644 --- a/sources/assets/Stride.Core.Assets/PackageSession.cs +++ b/sources/assets/Stride.Core.Assets/PackageSession.cs @@ -5,6 +5,7 @@ using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Text.RegularExpressions; using Stride.Core; using Stride.Core.Assets.Analysis; using Stride.Core.Assets.Diagnostics; @@ -787,10 +788,12 @@ public static void Load(string filePath, PackageSessionResult sessionResult, Pac SolutionProject? firstProject = null; // If we have a solution, load all packages - if (string.Equals(Path.GetExtension(filePath), ".sln", StringComparison.InvariantCultureIgnoreCase)) + if (Regex.IsMatch(Path.GetExtension(filePath), @"\.slnf?$", RegexOptions.IgnoreCase)) { // The session should save back its changes to the solution - var solution = session.VSSolution = VisualStudio.Solution.FromFile(filePath); + VisualStudio.Solution solution = session.VSSolution = Path.GetExtension(filePath).Equals(".sln", StringComparison.InvariantCultureIgnoreCase) + ? VisualStudio.Solution.FromFile(filePath) + : VisualStudio.Solution.FromSolutionFilter(filePath); // Keep header var versionHeader = solution.Properties.FirstOrDefault(x => x.Name == "VisualStudioVersion"); diff --git a/sources/assets/Stride.Core.Assets/PackageSessionHelper.Solution.cs b/sources/assets/Stride.Core.Assets/PackageSessionHelper.Solution.cs index d1291cdde9..b37c797edf 100644 --- a/sources/assets/Stride.Core.Assets/PackageSessionHelper.Solution.cs +++ b/sources/assets/Stride.Core.Assets/PackageSessionHelper.Solution.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Text.RegularExpressions; using NuGet.ProjectModel; using Stride.Core.Extensions; using Stride.Core.VisualStudio; @@ -18,33 +19,29 @@ internal partial class PackageSessionHelper { try { - // Solution file: extract projects - var solutionDirectory = Path.GetDirectoryName(fullPath) ?? ""; - var solution = Solution.FromFile(fullPath); - - foreach (var project in solution.Projects) + if (Path.GetExtension(fullPath).Equals(Package.PackageFileExtension, StringComparison.InvariantCultureIgnoreCase)) { - if (project.TypeGuid == KnownProjectTypeGuid.CSharp || project.TypeGuid == KnownProjectTypeGuid.CSharpNewSystem) + var packageVersion = await TryGetPackageVersion(fullPath); + if (packageVersion is not null) { - var projectPath = project.FullPath; - var projectAssetsJsonPath = Path.Combine(Path.GetDirectoryName(projectPath), "obj", LockFileFormat.AssetsFileName); -#if !STRIDE_LAUNCHER && !STRIDE_VSPACKAGE - if (!File.Exists(projectAssetsJsonPath)) - { - var log = new Stride.Core.Diagnostics.LoggerResult(); - await VSProjectHelper.RestoreNugetPackages(log, projectPath); - } -#endif - if (File.Exists(projectAssetsJsonPath)) + return packageVersion; + } + } + else if (Regex.IsMatch(Path.GetExtension(fullPath), @"\.slnf?$", RegexOptions.IgnoreCase)) + { + // Solution file: extract projects + var solution = Path.GetExtension(fullPath).Equals(".sln", StringComparison.InvariantCultureIgnoreCase) + ? Solution.FromFile(fullPath) + : Solution.FromSolutionFilter(fullPath); + foreach (var project in solution.Projects) + { + if (project.TypeGuid == KnownProjectTypeGuid.CSharp || project.TypeGuid == KnownProjectTypeGuid.CSharpNewSystem) { - var format = new LockFileFormat(); - var projectAssets = format.Read(projectAssetsJsonPath); - foreach (var library in projectAssets.Libraries) + var projectPath = project.FullPath; + var projectVersion = await TryGetPackageVersion(projectPath); + if (projectVersion is not null) { - if ((library.Type == "package" || library.Type == "project") && (library.Name == "Stride.Engine" || library.Name == "Xenko.Engine")) - { - return new PackageVersion(library.Version.ToString()); - } + return projectVersion; } } } @@ -92,4 +89,29 @@ internal static void RemovePackageSections(Project project) project.Sections.Remove(solutionPackageIdentifier); } } + + private static async Task TryGetPackageVersion(string projectPath) + { + var projectAssetsJsonPath = Path.Combine(Path.GetDirectoryName(projectPath), "obj", LockFileFormat.AssetsFileName); +#if !STRIDE_LAUNCHER && !STRIDE_VSPACKAGE + if (!File.Exists(projectAssetsJsonPath)) + { + var log = new Stride.Core.Diagnostics.LoggerResult(); + await VSProjectHelper.RestoreNugetPackages(log, projectPath); + } +#endif + if (File.Exists(projectAssetsJsonPath)) + { + var format = new LockFileFormat(); + var projectAssets = format.Read(projectAssetsJsonPath); + foreach (var library in projectAssets.Libraries) + { + if ((library.Type == "package" || library.Type == "project") && (library.Name == "Stride.Engine" || library.Name == "Xenko.Engine")) + { + return new PackageVersion(library.Version.ToString()); + } + } + } + return null; + } } diff --git a/sources/core/Stride.Core.Design/VisualStudio/Solution.cs b/sources/core/Stride.Core.Design/VisualStudio/Solution.cs index cc4c0a42c6..fcacc426af 100644 --- a/sources/core/Stride.Core.Design/VisualStudio/Solution.cs +++ b/sources/core/Stride.Core.Design/VisualStudio/Solution.cs @@ -195,4 +195,60 @@ public static Solution FromStream(string solutionFullPath, Stream stream) solution.FullPath = solutionFullPath; return solution; } + + /// + /// Loads a filtered solution from a solution filter file (.slnf). + /// + /// The solution filter full path. + /// A filtered Solution. + public static Solution FromSolutionFilter(string solutionFilterPath) + { + using var stream = new FileStream(solutionFilterPath, FileMode.Open, FileAccess.Read); + return FromSolutionFilterStream(solutionFilterPath, stream); + } + + /// + /// Loads a filtered solution from a solution filter stream (.slnf). + /// + /// The solution filter full path. + /// The stream containing the solution filter data. + /// A filtered Solution. + public static Solution FromSolutionFilterStream(string solutionFilterPath, Stream stream) + { + var solutionFilter = SolutionFilter.FromStream(solutionFilterPath, stream); + + // The solution filter contains a reference to the base solution + var baseSolutionPath = solutionFilter.SolutionPath; + var baseSolution = FromFile(baseSolutionPath); + + // Create a new solution with only the filtered projects + var filteredSolution = new Solution(); + filteredSolution.FullPath = baseSolution.FullPath; + filteredSolution.Headers.AddRange(baseSolution.Headers); + filteredSolution.Properties.AddRange(baseSolution.Properties); + filteredSolution.GlobalSections.AddRange(baseSolution.GlobalSections); + + // Build a dictionary for quick project path lookup + var projectPathMap = baseSolution.Projects + .Where(project => !project.IsSolutionFolder) + .ToDictionary( + project => project.GetRelativePath(baseSolution).Replace('/', Path.DirectorySeparatorChar), + project => project, + StringComparer.OrdinalIgnoreCase); + + // Add projects by path + foreach (var projectPath in solutionFilter.ProjectPaths) + { + if (projectPathMap.TryGetValue(projectPath, out var project)) + { + // Only add if not already added by GUID + if (!filteredSolution.Projects.Contains(project.Guid)) + { + filteredSolution.Projects.Add(project); + } + } + } + + return filteredSolution; + } } diff --git a/sources/core/Stride.Core.Design/VisualStudio/SolutionFilter.cs b/sources/core/Stride.Core.Design/VisualStudio/SolutionFilter.cs new file mode 100644 index 0000000000..2bcf9c1a82 --- /dev/null +++ b/sources/core/Stride.Core.Design/VisualStudio/SolutionFilter.cs @@ -0,0 +1,92 @@ +#region License + +// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// This file is distributed under MIT License. See LICENSE.md for details. + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Stride.Core.VisualStudio; + +/// +/// Represents a Visual Studio solution filter file (.slnf). +/// +public class SolutionFilter +{ + /// + /// Initializes a new instance of the class. + /// + public SolutionFilter() + { + SolutionPath = string.Empty; + ProjectPaths = []; + } + + /// + /// Gets or sets the path to the solution file referenced by this solution filter. + /// + public string SolutionPath { get; set; } + + /// + /// Gets the list of project paths included in the solution filter. + /// + public List ProjectPaths { get; } = []; + + /// + /// Loads a solution filter from a file path. + /// + /// The full path to the solution filter file. + /// A populated SolutionFilter instance. + public static SolutionFilter FromFile(string solutionFilterPath) + { + using var stream = new FileStream(solutionFilterPath, FileMode.Open, FileAccess.Read); + return FromStream(solutionFilterPath, stream); + } + + /// + /// Loads a solution filter from a stream. + /// + /// The full path to the solution filter file. + /// The stream containing the solution filter data. + /// A populated SolutionFilter instance. + public static SolutionFilter FromStream(string solutionFilterPath, Stream stream) + { + using var filterReader = new SolutionFilterReader(solutionFilterPath, stream); + return filterReader.ReadSolutionFilterFile(); + } +} + +/// +/// JSON model for deserializing solution filter files. +/// +internal class SolutionFilterData +{ + /// + /// Gets or sets the solution information. + /// + [JsonPropertyName("solution")] + public SolutionInfo? Solution { get; set; } + + /// + /// Represents solution information in a solution filter file. + /// + public class SolutionInfo + { + /// + /// Gets or sets the relative path to the solution file. + /// + [JsonPropertyName("path")] + public string? Path { get; set; } + + /// + /// Gets or sets the list of project paths in the solution filter. + /// + [JsonPropertyName("projects")] + public List? Projects { get; set; } + } +} diff --git a/sources/core/Stride.Core.Design/VisualStudio/SolutionFilterReader.cs b/sources/core/Stride.Core.Design/VisualStudio/SolutionFilterReader.cs new file mode 100644 index 0000000000..b359183a59 --- /dev/null +++ b/sources/core/Stride.Core.Design/VisualStudio/SolutionFilterReader.cs @@ -0,0 +1,109 @@ +#region License + +// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// This file is distributed under MIT License. See LICENSE.md for details. + +#endregion + +using System; +using System.IO; +using System.Text.Json; +using System.Text.RegularExpressions; + +namespace Stride.Core.VisualStudio; + +internal class SolutionFilterReader : IDisposable +{ + private readonly string solutionFilterPath; + private readonly string solutionFilterDirectory; + private StreamReader? reader; + private bool disposed; + + /// + /// Initializes a new instance of the class. + /// + /// The solution filter path. + public SolutionFilterReader(string solutionFilterPath) + : this(solutionFilterPath, new FileStream(solutionFilterPath, FileMode.Open, FileAccess.Read)) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The solution filter path. + /// The stream containing the solution filter data. + public SolutionFilterReader(string solutionFilterPath, Stream stream) + { + this.solutionFilterPath = solutionFilterPath; + solutionFilterDirectory = Path.GetDirectoryName(solutionFilterPath) ?? string.Empty; + reader = new StreamReader(stream); + } + + /// + /// Reads the solution filter file and returns a SolutionFilter instance. + /// + /// A populated SolutionFilter instance. + public SolutionFilter ReadSolutionFilterFile() + { +#if NET7_0_OR_GREATER + ObjectDisposedException.ThrowIf(disposed, this); +#else + if (disposed) throw new ObjectDisposedException(nameof(SolutionFilterReader)); +#endif + + var solutionFilter = new SolutionFilter(); + + try + { + // Read and deserialize the JSON content + var jsonContent = reader!.ReadToEnd(); + var options = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; + + var filterData = JsonSerializer.Deserialize(jsonContent, options); + + if (filterData?.Solution?.Path == null) + { + throw new SolutionFileException($"Invalid solution filter file: {solutionFilterPath}. Missing or invalid 'solution.path' property."); + } + + // Resolve the solution path relative to the solution filter + var relativeSolutionPath = filterData.Solution.Path.Replace('\\', Path.DirectorySeparatorChar); + solutionFilter.SolutionPath = Path.GetFullPath(Path.Combine(solutionFilterDirectory, relativeSolutionPath)); + + // Process project paths + if (filterData.Solution.Projects != null) + { + foreach (var projectPath in filterData.Solution.Projects) + { + if (!string.IsNullOrEmpty(projectPath)) + { + solutionFilter.ProjectPaths.Add(projectPath.Replace('\\', Path.DirectorySeparatorChar)); + } + } + } + } + catch (JsonException ex) + { + throw new SolutionFileException($"Error parsing solution filter file: {solutionFilterPath}", ex); + } + + return solutionFilter; + } + + /// + /// Disposes resources used by the reader. + /// + public void Dispose() + { + disposed = true; + if (reader != null) + { + reader.Dispose(); + reader = null; + } + } +} diff --git a/sources/editor/Stride.Core.Assets.Editor/Services/EditorDialogHelper.cs b/sources/editor/Stride.Core.Assets.Editor/Services/EditorDialogHelper.cs index b13b84768f..8d4b1c7f58 100644 --- a/sources/editor/Stride.Core.Assets.Editor/Services/EditorDialogHelper.cs +++ b/sources/editor/Stride.Core.Assets.Editor/Services/EditorDialogHelper.cs @@ -21,8 +21,9 @@ public static async Task BrowseForExistingProject(IViewModelServiceProvid var initialDirectory = InternalSettings.FileDialogLastOpenSessionDirectory.GetValue(); var filters = new List { - new("Solution or package files") { Patterns = [EditorViewModel.SolutionFileExtension, EditorViewModel.PackageFileExtension]}, + new("Solution or package files") { Patterns = [EditorViewModel.SolutionFileExtension, EditorViewModel.SolutionFilterFileExtension, EditorViewModel.PackageFileExtension]}, new("Solution file") { Patterns = [EditorViewModel.SolutionFileExtension]}, + new("Solution filter file") { Patterns = [EditorViewModel.SolutionFilterFileExtension]}, new("Package file") { Patterns = [EditorViewModel.PackageFileExtension]}, }; var filePaths = await OpenFileDialog(serviceProvider, false, initialDirectory, filters); diff --git a/sources/editor/Stride.Core.Assets.Editor/ViewModel/EditorViewModel.cs b/sources/editor/Stride.Core.Assets.Editor/ViewModel/EditorViewModel.cs index 848e986945..b291294ded 100644 --- a/sources/editor/Stride.Core.Assets.Editor/ViewModel/EditorViewModel.cs +++ b/sources/editor/Stride.Core.Assets.Editor/ViewModel/EditorViewModel.cs @@ -25,6 +25,7 @@ public abstract class EditorViewModel : ViewModelBase { public const string PackageFileExtension = Package.PackageFileExtension; public const string SolutionFileExtension = ".sln"; + public const string SolutionFilterFileExtension = ".slnf"; private SessionViewModel session; protected EditorViewModel(IViewModelServiceProvider serviceProvider, MostRecentlyUsedFileCollection mru, string editorName, string editorVersionMajor) From 864343e7f5f107d2a2559a9a7f9fb0b5a92ed1e0 Mon Sep 17 00:00:00 2001 From: Peter Laske <37439758+laske185@users.noreply.github.com> Date: Tue, 11 Mar 2025 19:31:01 +0100 Subject: [PATCH 2/4] refactor: Replace Regex with SolutionFileRegex for solution file detection, use naming policy for JSON deserialization and improve null checks --- sources/assets/Stride.Core.Assets/PackageSession.cs | 2 +- .../PackageSessionHelper.Solution.cs | 2 +- .../Stride.Core.Design/VisualStudio/Solution.cs | 3 +++ .../VisualStudio/SolutionFilter.cs | 9 --------- .../VisualStudio/SolutionFilterReader.cs | 13 +++++-------- 5 files changed, 10 insertions(+), 19 deletions(-) diff --git a/sources/assets/Stride.Core.Assets/PackageSession.cs b/sources/assets/Stride.Core.Assets/PackageSession.cs index 5c3199a59d..d992006a37 100644 --- a/sources/assets/Stride.Core.Assets/PackageSession.cs +++ b/sources/assets/Stride.Core.Assets/PackageSession.cs @@ -788,7 +788,7 @@ public static void Load(string filePath, PackageSessionResult sessionResult, Pac SolutionProject? firstProject = null; // If we have a solution, load all packages - if (Regex.IsMatch(Path.GetExtension(filePath), @"\.slnf?$", RegexOptions.IgnoreCase)) + if (VisualStudio.Solution.SolutionFileRegex.IsMatch(Path.GetExtension(filePath))) { // The session should save back its changes to the solution VisualStudio.Solution solution = session.VSSolution = Path.GetExtension(filePath).Equals(".sln", StringComparison.InvariantCultureIgnoreCase) diff --git a/sources/assets/Stride.Core.Assets/PackageSessionHelper.Solution.cs b/sources/assets/Stride.Core.Assets/PackageSessionHelper.Solution.cs index b37c797edf..1b23675b98 100644 --- a/sources/assets/Stride.Core.Assets/PackageSessionHelper.Solution.cs +++ b/sources/assets/Stride.Core.Assets/PackageSessionHelper.Solution.cs @@ -27,7 +27,7 @@ internal partial class PackageSessionHelper return packageVersion; } } - else if (Regex.IsMatch(Path.GetExtension(fullPath), @"\.slnf?$", RegexOptions.IgnoreCase)) + else if (Solution.SolutionFileRegex.IsMatch(Path.GetExtension(fullPath))) { // Solution file: extract projects var solution = Path.GetExtension(fullPath).Equals(".sln", StringComparison.InvariantCultureIgnoreCase) diff --git a/sources/core/Stride.Core.Design/VisualStudio/Solution.cs b/sources/core/Stride.Core.Design/VisualStudio/Solution.cs index fcacc426af..27df2f7ef8 100644 --- a/sources/core/Stride.Core.Design/VisualStudio/Solution.cs +++ b/sources/core/Stride.Core.Design/VisualStudio/Solution.cs @@ -24,6 +24,7 @@ #endregion using System.Diagnostics; +using System.Text.RegularExpressions; namespace Stride.Core.VisualStudio; @@ -33,6 +34,8 @@ namespace Stride.Core.VisualStudio; [DebuggerDisplay("Projects = [{Projects.Count}]")] public class Solution { + public static readonly Regex SolutionFileRegex = new(@"\.slnf?$", RegexOptions.IgnoreCase | RegexOptions.Compiled); + /// /// Initializes a new instance of the class. /// diff --git a/sources/core/Stride.Core.Design/VisualStudio/SolutionFilter.cs b/sources/core/Stride.Core.Design/VisualStudio/SolutionFilter.cs index 2bcf9c1a82..fe212a355c 100644 --- a/sources/core/Stride.Core.Design/VisualStudio/SolutionFilter.cs +++ b/sources/core/Stride.Core.Design/VisualStudio/SolutionFilter.cs @@ -5,12 +5,6 @@ #endregion -using System; -using System.Collections.Generic; -using System.IO; -using System.Text.Json; -using System.Text.Json.Serialization; - namespace Stride.Core.VisualStudio; /// @@ -69,7 +63,6 @@ internal class SolutionFilterData /// /// Gets or sets the solution information. /// - [JsonPropertyName("solution")] public SolutionInfo? Solution { get; set; } /// @@ -80,13 +73,11 @@ public class SolutionInfo /// /// Gets or sets the relative path to the solution file. /// - [JsonPropertyName("path")] public string? Path { get; set; } /// /// Gets or sets the list of project paths in the solution filter. /// - [JsonPropertyName("projects")] public List? Projects { get; set; } } } diff --git a/sources/core/Stride.Core.Design/VisualStudio/SolutionFilterReader.cs b/sources/core/Stride.Core.Design/VisualStudio/SolutionFilterReader.cs index b359183a59..12e1c0776a 100644 --- a/sources/core/Stride.Core.Design/VisualStudio/SolutionFilterReader.cs +++ b/sources/core/Stride.Core.Design/VisualStudio/SolutionFilterReader.cs @@ -5,14 +5,11 @@ #endregion -using System; -using System.IO; using System.Text.Json; -using System.Text.RegularExpressions; namespace Stride.Core.VisualStudio; -internal class SolutionFilterReader : IDisposable +internal sealed class SolutionFilterReader : IDisposable { private readonly string solutionFilterPath; private readonly string solutionFilterDirectory; @@ -60,12 +57,12 @@ public SolutionFilter ReadSolutionFilterFile() var jsonContent = reader!.ReadToEnd(); var options = new JsonSerializerOptions { - PropertyNameCaseInsensitive = true + PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; var filterData = JsonSerializer.Deserialize(jsonContent, options); - if (filterData?.Solution?.Path == null) + if (filterData?.Solution?.Path is null) { throw new SolutionFileException($"Invalid solution filter file: {solutionFilterPath}. Missing or invalid 'solution.path' property."); } @@ -75,7 +72,7 @@ public SolutionFilter ReadSolutionFilterFile() solutionFilter.SolutionPath = Path.GetFullPath(Path.Combine(solutionFilterDirectory, relativeSolutionPath)); // Process project paths - if (filterData.Solution.Projects != null) + if (filterData.Solution.Projects is not null) { foreach (var projectPath in filterData.Solution.Projects) { @@ -100,7 +97,7 @@ public SolutionFilter ReadSolutionFilterFile() public void Dispose() { disposed = true; - if (reader != null) + if (reader is not null) { reader.Dispose(); reader = null; From 50a115924fce1b4125103bf75ed8427ec91c8d4e Mon Sep 17 00:00:00 2001 From: Peter Laske <37439758+laske185@users.noreply.github.com> Date: Wed, 12 Mar 2025 12:34:06 +0100 Subject: [PATCH 3/4] fix: Include parent solution folders for projects in filtered solution --- .../Stride.Core.Design/VisualStudio/Solution.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sources/core/Stride.Core.Design/VisualStudio/Solution.cs b/sources/core/Stride.Core.Design/VisualStudio/Solution.cs index 27df2f7ef8..7989036914 100644 --- a/sources/core/Stride.Core.Design/VisualStudio/Solution.cs +++ b/sources/core/Stride.Core.Design/VisualStudio/Solution.cs @@ -251,6 +251,23 @@ public static Solution FromSolutionFilterStream(string solutionFilterPath, Strea } } } + + // Add solution folders that contain the included projects + var includedSolutionFolders = new HashSet(); + + // For each project, make sure its parent folders are included + foreach (var project in filteredSolution.Projects.ToList()) + { + var parent = project.GetParentProject(baseSolution); + while (parent != null) + { + if (includedSolutionFolders.Add(parent.Guid)) + { + filteredSolution.Projects.Add(parent); + } + parent = parent.GetParentProject(baseSolution); + } + } return filteredSolution; } From d53ecf0595da873f4f6946fb975e24617b370544 Mon Sep 17 00:00:00 2001 From: Peter Laske <37439758+laske185@users.noreply.github.com> Date: Wed, 12 Mar 2025 17:08:26 +0100 Subject: [PATCH 4/4] fix: Update filtered solution path assignment and improve null check for parent project retrieval --- sources/core/Stride.Core.Design/VisualStudio/Solution.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sources/core/Stride.Core.Design/VisualStudio/Solution.cs b/sources/core/Stride.Core.Design/VisualStudio/Solution.cs index 7989036914..349515191e 100644 --- a/sources/core/Stride.Core.Design/VisualStudio/Solution.cs +++ b/sources/core/Stride.Core.Design/VisualStudio/Solution.cs @@ -226,7 +226,7 @@ public static Solution FromSolutionFilterStream(string solutionFilterPath, Strea // Create a new solution with only the filtered projects var filteredSolution = new Solution(); - filteredSolution.FullPath = baseSolution.FullPath; + filteredSolution.FullPath = solutionFilterPath; filteredSolution.Headers.AddRange(baseSolution.Headers); filteredSolution.Properties.AddRange(baseSolution.Properties); filteredSolution.GlobalSections.AddRange(baseSolution.GlobalSections); @@ -259,7 +259,7 @@ public static Solution FromSolutionFilterStream(string solutionFilterPath, Strea foreach (var project in filteredSolution.Projects.ToList()) { var parent = project.GetParentProject(baseSolution); - while (parent != null) + while (parent is not null) { if (includedSolutionFolders.Add(parent.Guid)) {