-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Add support for creating and editing solution filter (.slnf) files from the CLI #51156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
d47f69c
146ee68
b8b8800
484dfcf
f2ad541
3c154af
4f0f20a
b3bc4e1
94e1bf8
23ea785
83a6e0e
70be5c2
75a51a8
db8b579
0e6f777
f264bd9
10f39ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,7 +45,7 @@ public SolutionAddCommand(ParseResult parseResult) : base(parseResult) | |
| _solutionFolderPath = parseResult.GetValue(SolutionAddCommandParser.SolutionFolderOption); | ||
| _includeReferences = parseResult.GetValue(SolutionAddCommandParser.IncludeReferencesOption); | ||
| SolutionArgumentValidator.ParseAndValidateArguments(_fileOrDirectory, _projects, SolutionArgumentValidator.CommandType.Add, _inRoot, _solutionFolderPath); | ||
| _solutionFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_fileOrDirectory); | ||
| _solutionFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_fileOrDirectory, includeSolutionFilterFiles: true); | ||
| } | ||
|
|
||
| public override int Execute() | ||
|
|
@@ -58,14 +58,22 @@ public override int Execute() | |
| // Get project paths from the command line arguments | ||
| PathUtility.EnsureAllPathsExist(_projects, CliStrings.CouldNotFindProjectOrDirectory, true); | ||
|
|
||
| IEnumerable<string> fullProjectPaths = _projects.Select(project => | ||
| List<string> fullProjectPaths = _projects.Select(project => | ||
| { | ||
| var fullPath = Path.GetFullPath(project); | ||
| return Directory.Exists(fullPath) ? MsbuildProject.GetProjectFileFromDirectory(fullPath).FullName : fullPath; | ||
| }); | ||
| }).ToList(); | ||
|
|
||
| // Add projects to the solution | ||
| AddProjectsToSolutionAsync(fullProjectPaths, CancellationToken.None).GetAwaiter().GetResult(); | ||
| // Check if we're working with a solution filter file | ||
| if (_solutionFileFullPath.HasExtension(".slnf")) | ||
| { | ||
| AddProjectsToSolutionFilterAsync(fullProjectPaths, CancellationToken.None).GetAwaiter().GetResult(); | ||
| } | ||
| else | ||
| { | ||
| // Add projects to the solution | ||
| AddProjectsToSolutionAsync(fullProjectPaths, CancellationToken.None).GetAwaiter().GetResult(); | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
|
|
@@ -224,4 +232,58 @@ private void AddProject(SolutionModel solution, string fullProjectPath, ISolutio | |
| } | ||
| } | ||
| } | ||
|
|
||
| private async Task AddProjectsToSolutionFilterAsync(IEnumerable<string> projectPaths, CancellationToken cancellationToken) | ||
| { | ||
| // Solution filter files don't support --in-root or --solution-folder options | ||
| if (_inRoot || !string.IsNullOrEmpty(_solutionFolderPath)) | ||
| { | ||
| throw new GracefulException(CliCommandStrings.SolutionFilterDoesNotSupportFolderOptions); | ||
| } | ||
|
|
||
| // Load the filtered solution to get the parent solution path and existing projects | ||
| SolutionModel filteredSolution = SlnFileFactory.CreateFromFilteredSolutionFile(_solutionFileFullPath); | ||
| string parentSolutionPath = filteredSolution.Description!; // The parent solution path is stored in Description | ||
|
|
||
| // Load the parent solution to validate projects exist in it | ||
| SolutionModel parentSolution = SlnFileFactory.CreateFromFileOrDirectory(parentSolutionPath); | ||
|
|
||
| // Get existing projects in the filter (already normalized to OS separator by CreateFromFilteredSolutionFile) | ||
| var existingProjects = filteredSolution.SolutionProjects.Select(p => p.FilePath).ToHashSet(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: On Windows, file paths are case-insensitive, but Consider using a case-insensitive comparer on Windows: var existingProjects = filteredSolution.SolutionProjects
.Select(p => p.FilePath)
.ToHashSet(StringComparer.OrdinalIgnoreCase);Or, if cross-platform behavior should match the OS: var comparer = OperatingSystem.IsWindows()
? StringComparer.OrdinalIgnoreCase
: StringComparer.Ordinal;
var existingProjects = filteredSolution.SolutionProjects
.Select(p => p.FilePath)
.ToHashSet(comparer);The same consideration applies to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 70be5c2. Added case-insensitive comparer on Windows using
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot - Actually, revert this to just use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reverted in commit that will be pushed shortly. Both |
||
|
|
||
| // Get solution-relative paths for new projects | ||
| var newProjects = new List<string>(); | ||
| string parentSolutionDirectory = Path.GetDirectoryName(parentSolutionPath) ?? string.Empty; | ||
| foreach (var projectPath in projectPaths) | ||
| { | ||
| string parentSolutionRelativePath = Path.GetRelativePath(parentSolutionDirectory, projectPath); | ||
|
|
||
| // Normalize to OS separator for consistent comparison | ||
| parentSolutionRelativePath = parentSolutionRelativePath.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar); | ||
|
|
||
| // Check if project exists in parent solution | ||
| var projectInParent = parentSolution.FindProject(parentSolutionRelativePath); | ||
| if (projectInParent is null) | ||
| { | ||
| Reporter.Error.WriteLine(CliStrings.ProjectNotFoundInTheSolution, parentSolutionRelativePath, parentSolutionPath); | ||
| continue; | ||
| } | ||
|
|
||
| // Check if project is already in the filter | ||
| if (existingProjects.Contains(parentSolutionRelativePath)) | ||
| { | ||
| Reporter.Output.WriteLine(CliStrings.SolutionAlreadyContainsProject, _solutionFileFullPath, parentSolutionRelativePath); | ||
| continue; | ||
| } | ||
|
|
||
| newProjects.Add(parentSolutionRelativePath); | ||
| Reporter.Output.WriteLine(CliStrings.ProjectAddedToTheSolution, parentSolutionRelativePath); | ||
| } | ||
|
mthalman marked this conversation as resolved.
|
||
|
|
||
| // Add new projects to the existing list and save | ||
| var allProjects = existingProjects.Concat(newProjects).OrderBy(p => p); | ||
| SlnfFileHelper.SaveSolutionFilter(_solutionFileFullPath, parentSolutionPath, allProjects); | ||
|
|
||
| await Task.CompletedTask; | ||
| } | ||
| } | ||
|
mthalman marked this conversation as resolved.
|
||
Uh oh!
There was an error while loading. Please reload this page.