Skip to content

AlignAssignmentStatement overhaul to fix issues and include handing of Enums.#2132

Open
liamjpeters wants to merge 3 commits intoPowerShell:mainfrom
liamjpeters:AlignAssignmentStatementV2
Open

AlignAssignmentStatement overhaul to fix issues and include handing of Enums.#2132
liamjpeters wants to merge 3 commits intoPowerShell:mainfrom
liamjpeters:AlignAssignmentStatementV2

Conversation

@liamjpeters
Copy link
Contributor

@liamjpeters liamjpeters commented Oct 1, 2025

PR Summary

Overhauls the AlignAssignmentStatement rule to include handling of enums as well as fix several issues with Hashtable alignment.

I am very open to feedback and suggestions 😀.

Setting defaults have changed. Previously the rule could be enabled, but not do anything, as CheckHashtable (previously the only setting and gating all functionality) defaulted to $false. I've changed this (as well as the new settings) to default to $true. If you enable the rule - it will do something now. You can then optionally disable any parts you don't want. The rule is opt-in and there are already breaking changes in this PR (see the baby and the bath water below). Docs update shows and explains new settings.

Hashtable alignment issues resolved include:

  • Erroring when the key of a key-value pair is an expression containing an equals sign

    @{
        ($Key1 = 5) = "Value1"
        Key2 = "Value2"
    }

    Invoke-ScriptAnalyzer: Start position cannot be before End position.

    This is because of the way the current implementation finds Equals tokens. It find the equals token within the expression.

    It results in a correction extent that has an end column which is before the start column.

  • Erasing inline comments when they appear between the key of the key-value pair and the Equals token.

    @{
        Key1 <#Sneakycomment#> = "Value1"
        Key2 = "Value2"
    }

    results in

    @{
        Key1 = "Value1"
        Key2 = "Value2"
    }
  • Performance scaling poorly. This is due to how Equals tokens of Key-Value pairs are located. For each Key-Value pair the list of tokens is scanned (from the beginning) until the relevant Equals token is found. This means that, as a worst case, performance scales quadratically - proportional to half the number of Key-Value pairs in the file * the number of tokens.

    We can improve this by scanning the tokens once and keeping track of all the Equals tokens and what line they appear on. This reduces complexity to essentially linear time with negligible memory use. Goes from O(H*T) -> O(H+T).

    I've put together some stress-test files (one saved as a gist here and script to generate them here) and ran only the AlignAssignmentStatement rule on them. Results below from my machine, averaged over 5 runs (with the first cold run discarded):

    Violations Pre-PR Post-PR
    100 Hashtables with 100 KVP 8,301 3.627s 0.221s
    500 Hashtables with 100 KVP 41,124 121.902s 1.533s
    1000 Hashtables with 100 KVP 82,147 457.873s 2.165s
  • The baby is thrown out with the bath water. If a single Key-Value pair is not in the expected format (Key and Equals-sign on same line, no two kvp on same line etc) the whole hashtable is skipped. I don't think this should be the case. The rule should consider and align the keys which are in the expected format and ignore the others. The rule is opt-in and so people using it want alignment - we should do our best to provide it.

I've done my best to expand the test coverage.

Fixes #1739
Fixes #1860

Would address downstream VSCode Extension issue: PowerShell/vscode-powershell#5138

PR Checklist

@TMA-2
Copy link

TMA-2 commented Oct 26, 2025

This is great! I won't pretend to know what you did regarding performance (I'm merely a PowerShell coder, not a C# programmer), but was hoping enums would be added to the PSAlignAssignmentStatement rule, so kudos! I just wrote a custom rule to handle this very issue, but there's no way it performs as well and strips comments.

@liamjpeters liamjpeters force-pushed the AlignAssignmentStatementV2 branch from db0240f to c57e789 Compare October 28, 2025 09:52
@liamjpeters
Copy link
Contributor Author

This is great! I won't pretend to know what you did regarding performance (I'm merely a PowerShell coder, not a C# programmer) ...

👋 @TMA-2

I'm also merely a PowerShell coder, not a C# programmer day-to-day 😀 - I'm sure it shows to those that are - but I'm interested and am giving it a go.

There's a lot to this PR, some desirable - some perhaps not 😅. I imagine it will be a while yet before anyone has the time to review it, and it may need to change quite a bit once it is.

@TMA-2
Copy link

TMA-2 commented Oct 28, 2025

This is great! I won't pretend to know what you did regarding performance (I'm merely a PowerShell coder, not a C# programmer) ...

👋 @TMA-2

I'm also merely a PowerShell coder, not a C# programmer day-to-day 😀 - I'm sure it shows to those that are - but I'm interested and am giving it a go.

There's a lot to this PR, some desirable - some perhaps not 😅. I imagine it will be a while yet before anyone has the time to review it, and it may need to change quite a bit once it is.

Certainly don't mean to downplay PowerShell -- I can't get enough, personally, despite all the gaps in the ecosystem that don't seem to exist with other languages. But nonetheless, lots of ingenious things come from the community that shouldn't seem possible (PODE alone is amazing!).

Here's hoping this gets a review quite soon. There's still an open PR for fixing unary operator formatting from June 17 with no comments, so the maintainers may be busy with other projects.

I could still attempt to take a look and see what I see, for what it's worth?

@bergmeister bergmeister requested a review from a team as a code owner January 30, 2026 17:03
Copy link
Collaborator

@bergmeister bergmeister left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the effort, I can see this was no easy feat.
It makes sense to move the gating to Enabled property (which should've been like that before) but we need to be careful, if there is any knock on effect on Vs-Code extension, which calls into rule here:
https://github.com/PowerShell/PowerShellEditorServices/blob/7b40f14d27f19b00069a10c8c2da02e3b9f59308/src/PowerShellEditorServices/Services/Workspace/LanguageServerSettings.cs#L298-L301
The value of AlignPropertyValuePairs that is used for CheckHashtable comes from here, with a default of true
https://github.com/PowerShell/vscode-powershell/blob/f3ee7362f11f70c2daf43a621cdc301cbde3f989/src/settings.ts#L94
Therefore we need to consider a PR there to either change default there as well and add new setting to let user control Enabled property OR adding settings for all the properties. As a start I'd say let's do just the former for simplicity. So far we've only mapped single user settings to individual PSSA settings as VS-Code didn't support rich objects as settings but I think that has changed now (?) but we don't need to boil the ocean for this but could be a candidate for the rule with object based vs code setting config. Don't get too frightened by this, we don't need to do this now, just when it comes to release time.

@liamjpeters
Copy link
Contributor Author

@bergmeister - I hadn't considered the upstream VSCode extension settings.

I don't think the default needs to change, in the extension/ES, for CheckHashtable - or expose the Enable value as a setting. It defaults already to Enabled = true (hardcoded in ES) and CheckHashtable = true. So it was enabling this rule and hashtable checking by default already.

If anyone has changed the hashtable setting to false in their settings file, the rule will continue to skip checking hashtables - regardless of Enabled = true.

The rule does introduce Enum Member Value Alignment (enabled by default), so perhaps we need to introduce that setting as a minimum?

If both CheckHashtable and CheckEnum are false, the rule effectively does nothing.

Something like the below could be a reasonable starting point.

image

I can raise the EditorServices and extension PRs? - at least it'll start a conversation and gather some more opinions 🙂

What do you think?


As an aside, something like the below in settings would allow full control of the rule. A quick Google, however, tells me the "nested" settings aren't really a thing 😔.

  • powershell > Code Formatting > Align Property Value Pairs

    Align assignment statements in a hashtable or a DSC Configuration

    • Include inline comments in Hashtable alignment

      Include Pairs with comments between the property and equals sign in alignment

  • powershell > Code Formatting > Align Enum Member Values

    Align member value assignments in an Enum Type Definition

    • Include inline comments in Enum alignment

      Include assignments with comments between the member and equals sign in alignment

    • Include valueless members in Enum alignment

      Include members without a value in the alignment, as if they had a value

@bergmeister
Copy link
Collaborator

@bergmeister - I hadn't considered the upstream VSCode extension settings.

I don't think the default needs to change, in the extension/ES, for CheckHashtable - or expose the Enable value as a setting. It defaults already to Enabled = true (hardcoded in ES) and CheckHashtable = true. So it was enabling this rule and hashtable checking by default already.

If anyone has changed the hashtable setting to false in their settings file, the rule will continue to skip checking hashtables - regardless of Enabled = true.

The rule does introduce Enum Member Value Alignment (enabled by default), so perhaps we need to introduce that setting as a minimum?

If both CheckHashtable and CheckEnum are false, the rule effectively does nothing.

Something like the below could be a reasonable starting point.

image I can raise the EditorServices and extension PRs? - at least it'll start a conversation and gather some more opinions 🙂

What do you think?

As an aside, something like the below in settings would allow full control of the rule. A quick Google, however, tells me the "nested" settings aren't really a thing 😔.

  • powershell > Code Formatting > Align Property Value Pairs

    Align assignment statements in a hashtable or a DSC Configuration

    • Include inline comments in Hashtable alignment

      Include Pairs with comments between the property and equals sign in alignment

  • powershell > Code Formatting > Align Enum Member Values

    Align member value assignments in an Enum Type Definition

    • Include inline comments in Enum alignment

      Include assignments with comments between the member and equals sign in alignment

    • Include valueless members in Enum alignment

      Include members without a value in the alignment, as if they had a value

The issue is more that before the user could turn rule on and off with user setting alignPropertyValuePairs but by changing the on/off switch to Enabled, it means it runs the new code for new options by default and cannot be turned off as there are currently no user settings for Enabled or the new options. That's why I think at minimum we need to give user ability to turn rule off if there is a bug in new code (experience showed, new formatting options often required iterations). But since this is quite an important rule that people rely on, I'd suggest adding the new option(s) as user settings so that a) users can take advantage of new features and also turn them off if needed (and we can as well by patching vs-code extension with different defaults). But like I said this is something we can do post the next PSSA release because the extension pins the PSSA version so we are in full control when to bump it and introduce new extension user setting to accommodate new release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extend PSAlignAssignmentStatement to include enum types Formatting does not properly align equal signs in enums

3 participants