Add -ExcludeProperty parameter to Format-* cmdlets#26514
Conversation
Implements PowerShell#25043 for Format-Table and Format-List cmdlets. Changes: - Add PSPropertyExpressionFilter class for wildcard pattern matching - Add -ExcludeProperty parameter to OuterFormatShapeCommandBase - Implement property filtering in TableViewGenerator and ListViewGenerator - Add tab completion support for -ExcludeProperty parameter - Add conflict check for -View and -ExcludeProperty parameters - Auto-apply '-Property *' when -ExcludeProperty is used without -Property - Change ListViewGenerator to use 'parameters' instead of 'inputParameters' Test Results: - Format-Table: 61 tests passed (56 existing + 5 new) - Format-List: 28 tests passed (23 existing + 5 new) - All Format-* tests: 174 tests passed Note: Format-Wide and Format-Custom will be implemented in subsequent commits.
Completes the implementation of ExcludeProperty parameter for all Format-* cmdlets (Table, List, Wide, Custom) to provide consistency with Select-Object's ExcludeProperty functionality. Changes: - Add ExcludeProperty parameter to Format-Wide and Format-Custom cmdlets - Implement property filtering in FormatViewGenerator_Wide and FormatViewGenerator_Complex - Add conflict check for -View and -ExcludeProperty parameters - Auto-apply '-Property *' when -ExcludeProperty is used without -Property - Change to use 'parameters' instead of 'inputParameters' for consistency - Remove unused 'inputParameters' field from base class - Add comprehensive test coverage (5 tests per cmdlet) Test Results: - All Format-* tests: 184 passed, 0 failed - Format-Wide: 19 tests (14 existing + 5 new) - Format-Custom: 5 new tests added
36939d8 to
c7e04d8
Compare
- Fix XML documentation to follow 'Gets or sets' convention - Add blank lines before single-line comments - Complete AutoSize property documentation summary
There was a problem hiding this comment.
Pull request overview
This PR adds the -ExcludeProperty parameter to all four Format-* cmdlets (Format-Table, Format-List, Format-Wide, and Format-Custom), providing a convenient way to exclude specific properties from formatted output without requiring an intermediate Select-Object call. The implementation follows a consistent pattern across all cmdlets, using wildcard pattern matching and automatically expanding to -Property * when used without an explicit -Property parameter.
Key Changes
- Introduced
PSPropertyExpressionFilterclass for wildcard-based property filtering - Added
-ExcludePropertyparameter to all Format-* cmdlets with proper validation and conflict detection - Integrated tab completion support for the new parameter
- Comprehensive test coverage with 20 new tests (5 per cmdlet)
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
BaseFormattingCommand.cs |
Defines PSPropertyExpressionFilter class for wildcard matching and adds -ExcludeProperty parameter to base cmdlet classes |
BaseFormattingCommandParameters.cs |
Adds excludePropertyFilter field to store the filter in command line parameters |
Format-Wide.cs |
Implements -ExcludeProperty parameter for Format-Wide cmdlet with auto-expansion logic |
Format-Object.cs |
Implements -ExcludeProperty parameter for Format-Custom cmdlet with auto-expansion logic |
FormatViewGenerator.cs |
Removes unused inputParameters field from base ViewGenerator class |
FormatViewGenerator_Table.cs |
Applies exclude filter at all property expansion points in table view generation |
FormatViewGenerator_List.cs |
Applies exclude filter after property setup; changes inputParameters to parameters |
FormatViewGenerator_Wide.cs |
Applies exclude filter at all property expansion points; removes unused inputParameters field |
FormatViewGenerator_Complex.cs |
Applies exclude filter in object display logic; refactors parameter handling |
CompletionCompleters.cs |
Adds tab completion support for -ExcludeProperty parameter on all Format-* cmdlets |
Format-Table.Tests.ps1 |
Adds 5 test cases covering basic exclusion, wildcards, auto-expansion, property combination, and multiple exclusions |
Format-List.Tests.ps1 |
Adds 5 test cases mirroring Format-Table coverage |
Format-Wide.Tests.ps1 |
Adds 5 test cases verifying first-property-after-exclusion behavior |
Format-Custom.Tests.ps1 |
Adds 5 test cases with custom format-specific assertions |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ParameterProcessor processor = new ParameterProcessor(def); | ||
| TerminatingErrorContext invocationContext = new TerminatingErrorContext(this); | ||
|
|
||
| parameters.mshParameterList = processor.ProcessParameters(new object[] { "*" }, invocationContext); |
There was a problem hiding this comment.
Not for the PR.
We have huge places in our code base where we allocate such temp arrays. We could review this and use modern C# feature params ReadOnlySpan<YYY> to pass arguments in methods without allocation.
There was a problem hiding this comment.
Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// <summary> | ||
| /// Filter for excluding properties from formatting. | ||
| /// </summary> | ||
| internal PSPropertyExpressionFilter excludePropertyFilter = null; |
There was a problem hiding this comment.
Question about the design. We don't keep property filter but only exclude filter. Is this really necessary? Looking how ApplyExcludePropertyFilter is inserted a question is - wouldn't it be more correct to apply the filter exactly at the time of adding new property in the list?
There was a problem hiding this comment.
Yes, storing the filter is necessary to pass it from the cmdlet to ViewGenerator. Here's the class structure:
Format-Table cmdlet
└── InnerFormatShapeCommand
└── _viewManager: FormatViewManager
└── _viewGenerator: ViewGenerator (TableViewGenerator, etc.)
└── activeAssociationList (property list)
ExcludePropertyparameter is defined in the cmdlet- Property list is built in
ViewGenerator.Initialize()using the input object (for wildcard expansion, DefaultPropertySet, etc.) ApplyExcludePropertyFilter()is defined inViewGenerator(the base class ofTableViewGenerator,ListViewGenerator, etc.)
Since the cmdlet and ViewGenerator are separate classes, we need a way to pass ExcludeProperty between them. ViewGenerator.Initialize() already receives FormattingCommandLineParameters, so adding excludePropertyFilter there was a natural way to pass it - same as -Property which is stored as mshParameterList in the same class.
Even with add-time filtering, we would still need to add a member to FormattingCommandLineParameters because that's the bridge between the cmdlet and ViewGenerator.
There was a problem hiding this comment.
My main concern is that we call ApplyExcludePropertyFilter() 8 times. It is looks as not reliable, it is easy to make an error.
Also we have some places where we access activeAssociationList and this rises a question should it return already filtered list?
There was a problem hiding this comment.
Thanks for the feedback. You're right - calling ApplyExcludePropertyFilter() at multiple exit points is error-prone.
I've refactored both methods to eliminate multiple return statements and call ApplyExcludePropertyFilter() exactly once at the end:
- Table
Initialize(): 3 calls → 1 call - Wide
SetUpActiveProperty(): 4 calls → 1 call
Regarding activeAssociationList access: The existing design doesn't prevent access to activeAssociationList before Initialize(), but I've verified no such access exists. All external access (e.g., GeneratePayload(), GenerateHeaderInfo()) happens after Initialize() completes, so they always see the filtered list.
There was a problem hiding this comment.
This is a good step forward. Thanks!
Nevertheless, can we think about making it more generic? Can it be in base.Initialize() for example? It would be great.
There was a problem hiding this comment.
Thank you for the positive feedback and the suggestion!
I explored moving ApplyExcludePropertyFilter() into base.Initialize(), but found some structural challenges:
Current Structure:
| ViewGenerator | When activeAssociationList is built |
|---|---|
| Table | During Initialize() |
| List | During Initialize() |
| Wide | Lazily in GeneratePayload() via SetUpActiveProperty() |
| Complex | Not used (uses ComplexViewObjectBrowser) |
Challenges with base.Initialize():
- Timing:
activeAssociationListis built by derived classes afterbase.Initialize()returns, so it would be null at that point - Wide's lazy initialization: Wide builds the list during
GeneratePayload(), not duringInitialize() - Complex doesn't use it: Uses a different rendering path
Possible approach - Template Method pattern:
// ViewGenerator (base)
internal virtual void Initialize(...) {
InitializeHelper();
BuildActiveAssociationList(so); // virtual, overridden by derived classes
ApplyExcludePropertyFilter();
}However, this wouldn't work for Wide's lazy initialization pattern without additional refactoring.
Current approach:
Each Initialize method calls ApplyExcludePropertyFilter() once at the end, which handles all cases including Wide's lazy pattern.
Would you prefer me to explore the Template Method approach further, or is the current single-call-per-Initialize approach acceptable given these constraints?
There was a problem hiding this comment.
Ideally it would be great to have clean class design. But if it requires a lot of refactoring, then it's better to postpone it, i.e. I can accept the PR as it is now, and you can do the refactoring in a subsequent PR if you want.
There was a problem hiding this comment.
Thank you for the flexibility and understanding!
I agree that a cleaner centralized approach would require some additional refactoring. The current approach—calling ApplyExcludePropertyFilter() once at the end of each Initialize method—keeps the changes minimal and focused on the -ExcludeProperty feature.
I've opened #26568 to track the potential refactoring for the future. Please feel free to assign it to me if you'd like.
There was a problem hiding this comment.
Feel free to grab the work. We are in beginning of next version dev cycle so we can do formatting and refactoring that way there's enough time to stabilize.
There was a problem hiding this comment.
Thank you! I'll take on #26568.
Since the refactoring depends on the code introduced in this PR, I'll start working on it after this PR is merged.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ies/Mshexpression.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
PR Summary
Add
-ExcludePropertyparameter to all Format-* cmdlets (Format-Table, Format-List, Format-Wide, and Format-Custom).PR Context
Implements #25043
The
-ExcludePropertyparameter allows users to exclude specific properties from formatting output without requiring an intermediateSelect-Objectcall, providing consistency withSelect-Object'sExcludePropertyfunctionality.Before this PR:
After this PR:
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerDescription
Implementation Overview
This PR implements the
-ExcludePropertyparameter for all four Format-* cmdlets across 2 commits:Files Changed: 14 files (10 product code, 4 test files)
Key Features
-ExcludeProperty Prop*)-Property, automatically applies-Property *-ViewparameterSelect-Object -ExcludePropertyfunctionalityWildcardOptions.IgnoreCaseCmdlet Behaviors
Usage Examples
Technical Implementation
PSPropertyExpressionFilterclass defined once inBaseFormattingCommand.cs, reused by all four cmdlets for wildcard pattern matchingactiveAssociationList.Where()) applied across all view generatorsCompletionCompleters.cs-Property *when-ExcludePropertyis used without-PropertyinputParametersfield during implementationTesting
Test Coverage:
Test Results:
Additional Notes
No Breaking Changes
Implementation Quality
Select-Object -ExcludeProperty