From 8a7847e933d690df307015fa2feeca59251b1fb0 Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Sun, 3 May 2020 18:43:09 +0100 Subject: [PATCH 1/8] Do not increase indentation after LParen the previous token is a Newline and the next token is not a Newline --- Rules/UseConsistentIndentation.cs | 61 ++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/Rules/UseConsistentIndentation.cs b/Rules/UseConsistentIndentation.cs index 8f17e2462..1586a1705 100644 --- a/Rules/UseConsistentIndentation.cs +++ b/Rules/UseConsistentIndentation.cs @@ -134,6 +134,8 @@ public override IEnumerable AnalyzeScript(Ast ast, string file var onNewLine = true; var pipelineAsts = ast.FindAll(testAst => testAst is PipelineAst && (testAst as PipelineAst).PipelineElements.Count > 1, true).ToList(); int minimumPipelineAstIndex = 0; + var parenthesisStack = new Stack(); + for (int tokenIndex = 0; tokenIndex < tokens.Length; tokenIndex++) { var token = tokens[tokenIndex]; @@ -146,10 +148,29 @@ public override IEnumerable AnalyzeScript(Ast ast, string file switch (token.Kind) { case TokenKind.AtCurly: - case TokenKind.AtParen: - case TokenKind.LParen: case TokenKind.LCurly: + AddViolation(token, indentationLevel++, diagnosticRecords, ref onNewLine); + break; + case TokenKind.DollarParen: + case TokenKind.AtParen: + parenthesisStack.Push(true); + AddViolation(token, indentationLevel++, diagnosticRecords, ref onNewLine); + break; + + case TokenKind.LParen: + // when a line start with a parenthesis then indentation does not need to be increased + if ((tokenIndex == 0 || tokens[tokenIndex - 1].Kind == TokenKind.NewLine) && + (tokenIndex < tokens.Length - 1 && NextTokenIgnoringComments(tokens, tokenIndex)?.Kind != TokenKind.NewLine)) + { + if (onNewLine) // left over from AddViolation + { + onNewLine = false; + } + parenthesisStack.Push(false); + break; + } + parenthesisStack.Push(true); AddViolation(token, indentationLevel++, diagnosticRecords, ref onNewLine); break; @@ -181,6 +202,19 @@ public override IEnumerable AnalyzeScript(Ast ast, string file break; case TokenKind.RParen: + parenthesisStack.TryPop(out bool matchingLParenIncreasedIndentation); + if (!matchingLParenIncreasedIndentation) + { + if (onNewLine) // left over from AddViolation + { + onNewLine = false; + } + break; + } + indentationLevel = ClipNegative(indentationLevel - 1); + AddViolation(token, indentationLevel, diagnosticRecords, ref onNewLine); + break; + case TokenKind.RCurly: indentationLevel = ClipNegative(indentationLevel - 1); AddViolation(token, indentationLevel, diagnosticRecords, ref onNewLine); @@ -254,6 +288,29 @@ public override IEnumerable AnalyzeScript(Ast ast, string file return diagnosticRecords; } + private static Token NextTokenIgnoringComments(Token[] tokens, int startIndex) + { + if (startIndex == tokens.Length - 1) + { + return null; + } + + for (int i = startIndex + 1; i < tokens.Length; i++) + { + switch (tokens[i].Kind) + { + case TokenKind.Comment: + continue; + + default: + return tokens[i]; + } + } + + // We've run out of tokens + return null; + } + private static bool PipelineIsFollowedByNewlineOrLineContinuation(Token[] tokens, int startIndex) { if (startIndex >= tokens.Length - 1) From 20c110a30de6a468258bd08ce4bde122c70afb9c Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Sun, 3 May 2020 19:05:47 +0100 Subject: [PATCH 2/8] TryPop not availabe in full .net. Also cleanup code --- Rules/UseConsistentIndentation.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Rules/UseConsistentIndentation.cs b/Rules/UseConsistentIndentation.cs index 1586a1705..7c4b7ce42 100644 --- a/Rules/UseConsistentIndentation.cs +++ b/Rules/UseConsistentIndentation.cs @@ -134,7 +134,7 @@ public override IEnumerable AnalyzeScript(Ast ast, string file var onNewLine = true; var pipelineAsts = ast.FindAll(testAst => testAst is PipelineAst && (testAst as PipelineAst).PipelineElements.Count > 1, true).ToList(); int minimumPipelineAstIndex = 0; - var parenthesisStack = new Stack(); + var lParenSkippedIndentation = new Stack(); for (int tokenIndex = 0; tokenIndex < tokens.Length; tokenIndex++) { @@ -154,7 +154,7 @@ public override IEnumerable AnalyzeScript(Ast ast, string file case TokenKind.DollarParen: case TokenKind.AtParen: - parenthesisStack.Push(true); + lParenSkippedIndentation.Push(false); AddViolation(token, indentationLevel++, diagnosticRecords, ref onNewLine); break; @@ -167,10 +167,10 @@ public override IEnumerable AnalyzeScript(Ast ast, string file { onNewLine = false; } - parenthesisStack.Push(false); + lParenSkippedIndentation.Push(true); break; } - parenthesisStack.Push(true); + lParenSkippedIndentation.Push(false); AddViolation(token, indentationLevel++, diagnosticRecords, ref onNewLine); break; @@ -202,10 +202,14 @@ public override IEnumerable AnalyzeScript(Ast ast, string file break; case TokenKind.RParen: - parenthesisStack.TryPop(out bool matchingLParenIncreasedIndentation); - if (!matchingLParenIncreasedIndentation) + bool matchingLParenIncreasedIndentation = false; + if (lParenSkippedIndentation.Count > 0) { - if (onNewLine) // left over from AddViolation + matchingLParenIncreasedIndentation = lParenSkippedIndentation.Pop(); + } + if (matchingLParenIncreasedIndentation) + { + if (onNewLine) { onNewLine = false; } From 194022d2f04bd459c1c56f4db52d72a95b102715 Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Sun, 3 May 2020 19:25:32 +0100 Subject: [PATCH 3/8] add test cases --- .../Rules/UseConsistentIndentation.tests.ps1 | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/Tests/Rules/UseConsistentIndentation.tests.ps1 b/Tests/Rules/UseConsistentIndentation.tests.ps1 index 3f527b2f0..6f41536ec 100644 --- a/Tests/Rules/UseConsistentIndentation.tests.ps1 +++ b/Tests/Rules/UseConsistentIndentation.tests.ps1 @@ -109,6 +109,72 @@ $param3 } } + Context 'LParen indentation' { + It 'Should preserve script when line starts with LParen' { + $IdempotentScriptDefinition = @' +function test { + (foo | bar { + baz + }) + Do-Something +} +'@ + Invoke-Formatter -ScriptDefinition $IdempotentScriptDefinition | Should -Be $idempotentScriptDefinition + } + + It 'Should preserve script when line starts and ends with LParen' { + $IdempotentScriptDefinition = @' +function test { + ( + foo | bar { + baz + } + ) + Do-Something +} +'@ + Invoke-Formatter -ScriptDefinition $IdempotentScriptDefinition | Should -Be $idempotentScriptDefinition + } + + It 'Should preserve script when line starts and ends with LParen but trailing comment' { + $IdempotentScriptDefinition = @' +function test { + ( # comment + foo | bar { + baz + } + ) + Do-Something +} +'@ + Invoke-Formatter -ScriptDefinition $IdempotentScriptDefinition | Should -Be $idempotentScriptDefinition + } + + It 'Should preserve script when there is Newline after LParen' { + $IdempotentScriptDefinition = @' +function test { + $result = ( + Get-Something + ).Property + Do-Something +} +'@ + Invoke-Formatter -ScriptDefinition $IdempotentScriptDefinition | Should -Be $idempotentScriptDefinition + } + + It 'Should preserve script when there is a comment and Newline after LParen' { + $IdempotentScriptDefinition = @' +function test { + $result = ( # comment + Get-Something + ).Property + Do-Something +} +'@ + Invoke-Formatter -ScriptDefinition $IdempotentScriptDefinition | Should -Be $idempotentScriptDefinition + } +} + Context "When a sub-expression is provided" { It "Should not find a violations" { $def = @' From bd738a7e37d6f54c19139893b0fa6100ab41b1bf Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Sun, 3 May 2020 19:28:02 +0100 Subject: [PATCH 4/8] cleanup code comment --- Rules/UseConsistentIndentation.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Rules/UseConsistentIndentation.cs b/Rules/UseConsistentIndentation.cs index 7c4b7ce42..63eb1ac35 100644 --- a/Rules/UseConsistentIndentation.cs +++ b/Rules/UseConsistentIndentation.cs @@ -159,11 +159,12 @@ public override IEnumerable AnalyzeScript(Ast ast, string file break; case TokenKind.LParen: - // when a line start with a parenthesis then indentation does not need to be increased + // When a line start with a parenthesis and it is not the last non-comment token of that line, + // then indentation does not need to be increased. if ((tokenIndex == 0 || tokens[tokenIndex - 1].Kind == TokenKind.NewLine) && (tokenIndex < tokens.Length - 1 && NextTokenIgnoringComments(tokens, tokenIndex)?.Kind != TokenKind.NewLine)) { - if (onNewLine) // left over from AddViolation + if (onNewLine) { onNewLine = false; } From 607615c41e4c1a68f75a635c422be9279fec0240 Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Mon, 4 May 2020 18:42:28 +0100 Subject: [PATCH 5/8] re-trigger ci --- pssa-1.19.0-blog.md | 64 +++++++++++++++++++++++++++++++++++++++++++++ test.ps1 | 20 ++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 pssa-1.19.0-blog.md create mode 100644 test.ps1 diff --git a/pssa-1.19.0-blog.md b/pssa-1.19.0-blog.md new file mode 100644 index 000000000..06228f991 --- /dev/null +++ b/pssa-1.19.0-blog.md @@ -0,0 +1,64 @@ +# PSScriptAnalyzer (PSSA) 1.19.0 has been released + +## TL;DR; (Too Long; Didn’t Read) + +This new minor version brings 5 new rules, the formatter is much faster and other enhancements and fixes. You can get it from the PSGallery [here](https://www.powershellgallery.com/packages/PSScriptAnalyzer/1.19.0). Pending no blocking feedback, the PowerShell VS-Code extension will ship with this version soon as well. For more details, read below or see the full changelog [here](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/CHANGELOG.MD). + +## New Script Analysis Rules + +There are a total of five new analyzer rules and they were all added by the community! + +- [AvoidLongLines](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/AvoidLongLines.md) (thanks [Thomas Rayner](https://twitter.com/MrThomasRayner)!): Warns when a line is too long (default is 120 characters) but is not enabled by default. +- [AvoidOverwritingBuiltInCmdlets](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/AvoidOverwritingBuiltInCmdlets.md) (thanks [Thomas Rayner](https://twitter.com/MrThomasRayner)!): Warns you if you accidentally try to re-define a built-in cmdlet such as e.g. `Get-Item` by writing code like e.g. `function Get-Item { }`. +- [UseUsingScopeModifierInNewRunspaces](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/UseUsingScopeModifierInNewRunspaces.md) (thanks [Jos Koelewijn](https://twitter.com/Jawz_84)!): Warns you when trying to incorrectly reference a variable that is not defined directly in the scope of the current scriptblock. This applies e.g. to the `-Command` parameter of `Invoke-Command` or `-Parallel` of `ForEach-Object` (added in PowerShell 7, see blog [here](https://devblogs.microsoft.com/powershell/powershell-foreach-object-parallel-feature/)). It recommends to use the `$using` scope modifier to reference the variable correcty. +- [UseProcessBlockForPipelineCommand](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/UseProcessBlockForPipelineCommand.md) (thanks [Matt McNabb](https://twitter.com/mcnabbmh)!): Warns when a function declares support for pipeline input (via the `[Parameter(ValueFromPipeline)]` attribute) that the function needs to declare a `process { }` block. +- [ReviewUnusedParameter](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/ReviewUnusedParameter.md) (thanks [Matt McNabb](https://twitter.com/mcnabbmh)!): Warns on parameters declared in a script, scriptblock, or function scope that have not been used in that scope. You can think of it as the equivalent of the `UseDeclaredVarsMoreThanAssignments` rule but for parameters instead of variables. + +The added computational work for those rules are being compensated for by a performance improvement in the `AvoidTrailingWhitespace` rule, therefore the speed of running `Invoke-ScriptAnalyzer` for all rules is roughly the same compared to the previous version of PSSScriptAnalyzer. + +## Formatter + +### Performance + +Several improvements have been made to the formatting rules and the engine that should result in the formatter being multiple times faster, especially for large scripts. The improvements were: + +- Reduced initialisation overhead. +- Improved efficiency of formatting rules, which addresses the scaling problems that were seen for large scripts. +- Reduce the number of times the script text has to be re-parsed. In its current architecture, the formatter has to re-parse the script after every rule run and every applied correction. When a rule has no violations then the parsed AST and tokens are being recycled. This leads to faster formatting when no or only some correction have to be made. + +To give a specific example, running the formatter on [PowerShell](https://github.com/PowerShell/PowerShell)'s [build.psm1](https://github.com/PowerShell/PowerShell/blob/master/build.psm1) module, which has over 3000 lines, we've measure the following times for a 'warm' run: + +- Version 1.18.3 takes around 1,250 ms. +- Version 1.19.0 takes around 550 ms. +- Version 1.19.0 takes around 170 ms on the pre-formatted file. + +The reason why we measure a 'warm' run is because in the editor scenario, the PowerShell extension will have likely already finished a run `Invoke-ScriptAnalyzer` in the background on the script, which means the [CommandInfo](https://docs.microsoft.com/dotnet/api/system.management.automation.commandinfo) cache has already been populated. Although the default set of formatting rules in PowerShell's VS-Code extension doesn't need the CommandInfo cache and therefore there wouldn't be much of a difference between cold and warm for most users, the following 2 optional rules do use the CommandInfo: + +- [UseCorrectCasing](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/UseCorrectCasing.md): Controlled via the `powershell.codeFormatting.useCorrectCasing` PowerShell VS-Code extension setting. +- [AvoidUsingCmdletAliases](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/AvoidUsingCmdletAliases.md): Controlled via the `powershell.codeFormatting.autoCorrectAliases` PowerShell VS-Code extension setting. + +### New Formatter features + +- Parameter Casing: The [UseCorrectCasing](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/UseCorrectCasing.md) rule now correct also the casing of parameters and not just the cmdlet names. +- The `PipelineIndentationStyle` setting of `UseConsistentIndentation` has a new option now and will also become the default in PowerShell's VS-Code extension. This new option is named `None` and means that it does not change the user's pipeline indentation at all and therefore change this feature to be an opt-in scenario. For the previous non-default settings `IncreaseIndentationForFirstPipeline` and `IncreaseIndentationAfterEveryPipeline` all new user reported bugs are fixed now and therefore we encourage you to please try it out again. +- The [UseConsistentWhitespace](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/UseConsistentWhitespace.md) option `CheckPipe` has not been changed to only ADD whitespace around the pipe if it is missing but not remove extranous whitespace. This is because some people prefer to line up their pipelines in Pester tests. For anyone who still wants to trim redundant whitespace around pipes, the new `CheckPipeForRedundantWhitespace` option has been provided. For VS-Code users this means that we plan to split the existing `powershell.codeFormatting.whitespaceAroundPipe` (enabled by default) setting into 2 new ones: `powershell.codeFormatting.addWhitespaceAroundPipe` (enabled by default) and `powershell.codeFormatting.trimWhitespaceAroundPipe` (disabled by default). +- The [UseConsistentWhitespace](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/UseConsistentWhitespace.md) option `CheckParameter` has been added and can be controlled via the VS-Code setting `powershell.codeFormatting.whitespaceBetweenParameters`. + +## Other improvements + +- The compatibility rules were updated to includes profiles for PowerShell 7, support single number version strings now as well and also have added analysis for PowerShell 7 syntax (null-conditional method invocation, null-coalescing operator, ternary expression and pipeline chain operator). +- [AvoidAssignmentToAutomaticVariable](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/AvoidAssignmentToAutomaticVariable.md) rule was enanced to include now not only warn on read-only [automatic variables](https://docs.microsoft.com/powershell/module/microsoft.powershell.core/about/about_automatic_variables), but also other automatic variables that can but should not be assigned to, which also includes the commonly misused `$input` variable. +- The minimum supported version of PowerShell Core is `6.2.4` now but please bear in mind that support for Powershell `6.2` itself ends on September 04 2020, see support policy [here](https://docs.microsoft.com/en-us/powershell/scripting/powershell-support-lifecycle?view=powershell-7#powershell-releases-end-of-life). +- Our CI system has been migrated to use multi-stage Azure Pipelines, meaning that every commit gets tested against `Windows` (Server 2016 and 2019), `Ubuntu` (`16.04` and `18.04`) and `macOS` (`10.14` and `10.15`) for PowerShell 7 and also Windows PowerShell 5.1. The previous AppVeyor build still provides coverage for PowerShell 4 and before the release a manual test was executed to guarantee functionality for PowerShell 3. + +## Outlook + +As mentioned in multiple, previous posts, the PowerShell teams is looking at a partial re-write of PSScriptAnalyzer, which will likely require a bump of the major version. Therefore `1.19.0` might be the last version of `1.x`, maybe with the exception of a patch. In version `2.x`, support for PowerShell 3 and 4 is likely going to drop. + +On behalf of the Script Analyzer team, + +[Christoph Bergmeister](https://twitter.com/CBergmeister), Project Maintainer from the community, [BJSS](https://www.bjss.com/) + +[Jim Truher](https://twitter.com/jwtruher), Senior Software Engineer, Microsoft + +[Rob Holt](https://twitter.com/rjmholt), Software Engineer on the PowerShell team, Microsoft diff --git a/test.ps1 b/test.ps1 new file mode 100644 index 000000000..84452c378 --- /dev/null +++ b/test.ps1 @@ -0,0 +1,20 @@ +Get-ChildItem -path $path +Get-ChildItem -path $path | Should -Be $exp +Get-ChildItem -path $path | Should -Be $exp +foo|bar # addWhitespaceAroundPipe +foo | bar # trimWhitespaceAroundPipe +foo -barc # whitespaceBetweenParameters +if ($true) {} + +foo | + bar + +foo | + bar + +foo | +bar + +foo | +bar | +baz From a47555f6d5ce77d4f32f8d250addb1fe25cc2e31 Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Tue, 21 Jul 2020 20:46:14 +0100 Subject: [PATCH 6/8] remove accidentally checked in pssa 1.19.0 blog --- pssa-1.19.0-blog.md | 64 --------------------------------------------- 1 file changed, 64 deletions(-) delete mode 100644 pssa-1.19.0-blog.md diff --git a/pssa-1.19.0-blog.md b/pssa-1.19.0-blog.md deleted file mode 100644 index 06228f991..000000000 --- a/pssa-1.19.0-blog.md +++ /dev/null @@ -1,64 +0,0 @@ -# PSScriptAnalyzer (PSSA) 1.19.0 has been released - -## TL;DR; (Too Long; Didn’t Read) - -This new minor version brings 5 new rules, the formatter is much faster and other enhancements and fixes. You can get it from the PSGallery [here](https://www.powershellgallery.com/packages/PSScriptAnalyzer/1.19.0). Pending no blocking feedback, the PowerShell VS-Code extension will ship with this version soon as well. For more details, read below or see the full changelog [here](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/CHANGELOG.MD). - -## New Script Analysis Rules - -There are a total of five new analyzer rules and they were all added by the community! - -- [AvoidLongLines](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/AvoidLongLines.md) (thanks [Thomas Rayner](https://twitter.com/MrThomasRayner)!): Warns when a line is too long (default is 120 characters) but is not enabled by default. -- [AvoidOverwritingBuiltInCmdlets](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/AvoidOverwritingBuiltInCmdlets.md) (thanks [Thomas Rayner](https://twitter.com/MrThomasRayner)!): Warns you if you accidentally try to re-define a built-in cmdlet such as e.g. `Get-Item` by writing code like e.g. `function Get-Item { }`. -- [UseUsingScopeModifierInNewRunspaces](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/UseUsingScopeModifierInNewRunspaces.md) (thanks [Jos Koelewijn](https://twitter.com/Jawz_84)!): Warns you when trying to incorrectly reference a variable that is not defined directly in the scope of the current scriptblock. This applies e.g. to the `-Command` parameter of `Invoke-Command` or `-Parallel` of `ForEach-Object` (added in PowerShell 7, see blog [here](https://devblogs.microsoft.com/powershell/powershell-foreach-object-parallel-feature/)). It recommends to use the `$using` scope modifier to reference the variable correcty. -- [UseProcessBlockForPipelineCommand](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/UseProcessBlockForPipelineCommand.md) (thanks [Matt McNabb](https://twitter.com/mcnabbmh)!): Warns when a function declares support for pipeline input (via the `[Parameter(ValueFromPipeline)]` attribute) that the function needs to declare a `process { }` block. -- [ReviewUnusedParameter](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/ReviewUnusedParameter.md) (thanks [Matt McNabb](https://twitter.com/mcnabbmh)!): Warns on parameters declared in a script, scriptblock, or function scope that have not been used in that scope. You can think of it as the equivalent of the `UseDeclaredVarsMoreThanAssignments` rule but for parameters instead of variables. - -The added computational work for those rules are being compensated for by a performance improvement in the `AvoidTrailingWhitespace` rule, therefore the speed of running `Invoke-ScriptAnalyzer` for all rules is roughly the same compared to the previous version of PSSScriptAnalyzer. - -## Formatter - -### Performance - -Several improvements have been made to the formatting rules and the engine that should result in the formatter being multiple times faster, especially for large scripts. The improvements were: - -- Reduced initialisation overhead. -- Improved efficiency of formatting rules, which addresses the scaling problems that were seen for large scripts. -- Reduce the number of times the script text has to be re-parsed. In its current architecture, the formatter has to re-parse the script after every rule run and every applied correction. When a rule has no violations then the parsed AST and tokens are being recycled. This leads to faster formatting when no or only some correction have to be made. - -To give a specific example, running the formatter on [PowerShell](https://github.com/PowerShell/PowerShell)'s [build.psm1](https://github.com/PowerShell/PowerShell/blob/master/build.psm1) module, which has over 3000 lines, we've measure the following times for a 'warm' run: - -- Version 1.18.3 takes around 1,250 ms. -- Version 1.19.0 takes around 550 ms. -- Version 1.19.0 takes around 170 ms on the pre-formatted file. - -The reason why we measure a 'warm' run is because in the editor scenario, the PowerShell extension will have likely already finished a run `Invoke-ScriptAnalyzer` in the background on the script, which means the [CommandInfo](https://docs.microsoft.com/dotnet/api/system.management.automation.commandinfo) cache has already been populated. Although the default set of formatting rules in PowerShell's VS-Code extension doesn't need the CommandInfo cache and therefore there wouldn't be much of a difference between cold and warm for most users, the following 2 optional rules do use the CommandInfo: - -- [UseCorrectCasing](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/UseCorrectCasing.md): Controlled via the `powershell.codeFormatting.useCorrectCasing` PowerShell VS-Code extension setting. -- [AvoidUsingCmdletAliases](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/AvoidUsingCmdletAliases.md): Controlled via the `powershell.codeFormatting.autoCorrectAliases` PowerShell VS-Code extension setting. - -### New Formatter features - -- Parameter Casing: The [UseCorrectCasing](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/UseCorrectCasing.md) rule now correct also the casing of parameters and not just the cmdlet names. -- The `PipelineIndentationStyle` setting of `UseConsistentIndentation` has a new option now and will also become the default in PowerShell's VS-Code extension. This new option is named `None` and means that it does not change the user's pipeline indentation at all and therefore change this feature to be an opt-in scenario. For the previous non-default settings `IncreaseIndentationForFirstPipeline` and `IncreaseIndentationAfterEveryPipeline` all new user reported bugs are fixed now and therefore we encourage you to please try it out again. -- The [UseConsistentWhitespace](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/UseConsistentWhitespace.md) option `CheckPipe` has not been changed to only ADD whitespace around the pipe if it is missing but not remove extranous whitespace. This is because some people prefer to line up their pipelines in Pester tests. For anyone who still wants to trim redundant whitespace around pipes, the new `CheckPipeForRedundantWhitespace` option has been provided. For VS-Code users this means that we plan to split the existing `powershell.codeFormatting.whitespaceAroundPipe` (enabled by default) setting into 2 new ones: `powershell.codeFormatting.addWhitespaceAroundPipe` (enabled by default) and `powershell.codeFormatting.trimWhitespaceAroundPipe` (disabled by default). -- The [UseConsistentWhitespace](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/UseConsistentWhitespace.md) option `CheckParameter` has been added and can be controlled via the VS-Code setting `powershell.codeFormatting.whitespaceBetweenParameters`. - -## Other improvements - -- The compatibility rules were updated to includes profiles for PowerShell 7, support single number version strings now as well and also have added analysis for PowerShell 7 syntax (null-conditional method invocation, null-coalescing operator, ternary expression and pipeline chain operator). -- [AvoidAssignmentToAutomaticVariable](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/AvoidAssignmentToAutomaticVariable.md) rule was enanced to include now not only warn on read-only [automatic variables](https://docs.microsoft.com/powershell/module/microsoft.powershell.core/about/about_automatic_variables), but also other automatic variables that can but should not be assigned to, which also includes the commonly misused `$input` variable. -- The minimum supported version of PowerShell Core is `6.2.4` now but please bear in mind that support for Powershell `6.2` itself ends on September 04 2020, see support policy [here](https://docs.microsoft.com/en-us/powershell/scripting/powershell-support-lifecycle?view=powershell-7#powershell-releases-end-of-life). -- Our CI system has been migrated to use multi-stage Azure Pipelines, meaning that every commit gets tested against `Windows` (Server 2016 and 2019), `Ubuntu` (`16.04` and `18.04`) and `macOS` (`10.14` and `10.15`) for PowerShell 7 and also Windows PowerShell 5.1. The previous AppVeyor build still provides coverage for PowerShell 4 and before the release a manual test was executed to guarantee functionality for PowerShell 3. - -## Outlook - -As mentioned in multiple, previous posts, the PowerShell teams is looking at a partial re-write of PSScriptAnalyzer, which will likely require a bump of the major version. Therefore `1.19.0` might be the last version of `1.x`, maybe with the exception of a patch. In version `2.x`, support for PowerShell 3 and 4 is likely going to drop. - -On behalf of the Script Analyzer team, - -[Christoph Bergmeister](https://twitter.com/CBergmeister), Project Maintainer from the community, [BJSS](https://www.bjss.com/) - -[Jim Truher](https://twitter.com/jwtruher), Senior Software Engineer, Microsoft - -[Rob Holt](https://twitter.com/rjmholt), Software Engineer on the PowerShell team, Microsoft From 5f2201efd85d551bdd94eddf576862453a4bd8b2 Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Tue, 21 Jul 2020 20:55:21 +0100 Subject: [PATCH 7/8] Simplify 'if (onNewLine) { onNewLine = false; }' to 'onNewLine = false;' and fix spelling in comment --- Rules/UseConsistentIndentation.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Rules/UseConsistentIndentation.cs b/Rules/UseConsistentIndentation.cs index 93716f47b..f8cd25087 100644 --- a/Rules/UseConsistentIndentation.cs +++ b/Rules/UseConsistentIndentation.cs @@ -159,15 +159,12 @@ public override IEnumerable AnalyzeScript(Ast ast, string file break; case TokenKind.LParen: - // When a line start with a parenthesis and it is not the last non-comment token of that line, + // When a line starts with a parenthesis and it is not the last non-comment token of that line, // then indentation does not need to be increased. if ((tokenIndex == 0 || tokens[tokenIndex - 1].Kind == TokenKind.NewLine) && (tokenIndex < tokens.Length - 1 && NextTokenIgnoringComments(tokens, tokenIndex)?.Kind != TokenKind.NewLine)) { - if (onNewLine) - { - onNewLine = false; - } + onNewLine = false; lParenSkippedIndentation.Push(true); break; } @@ -210,10 +207,7 @@ public override IEnumerable AnalyzeScript(Ast ast, string file } if (matchingLParenIncreasedIndentation) { - if (onNewLine) - { - onNewLine = false; - } + onNewLine = false; break; } indentationLevel = ClipNegative(indentationLevel - 1); From b3bf36d65e1a2be71c656e006a93086279da45b5 Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Wed, 22 Jul 2020 16:38:49 +0100 Subject: [PATCH 8/8] - Simplify token checking code and add comment around stack - Remove accidentally checked in file --- Rules/UseConsistentIndentation.cs | 9 +++++++-- test.ps1 | 20 -------------------- 2 files changed, 7 insertions(+), 22 deletions(-) delete mode 100644 test.ps1 diff --git a/Rules/UseConsistentIndentation.cs b/Rules/UseConsistentIndentation.cs index f8cd25087..3fc7aaa9b 100644 --- a/Rules/UseConsistentIndentation.cs +++ b/Rules/UseConsistentIndentation.cs @@ -134,6 +134,11 @@ public override IEnumerable AnalyzeScript(Ast ast, string file var onNewLine = true; var pipelineAsts = ast.FindAll(testAst => testAst is PipelineAst && (testAst as PipelineAst).PipelineElements.Count > 1, true).ToList(); int minimumPipelineAstIndex = 0; + /* + When an LParen and LBrace are on the same line, it can lead to too much de-indentation. + In order to prevent the RParen code from de-indenting too much, we keep a stack of when we skipped the indentation + caused by tokens that require a closing RParen (which are LParen, AtParen and DollarParen). + */ var lParenSkippedIndentation = new Stack(); for (int tokenIndex = 0; tokenIndex < tokens.Length; tokenIndex++) @@ -162,7 +167,7 @@ public override IEnumerable AnalyzeScript(Ast ast, string file // When a line starts with a parenthesis and it is not the last non-comment token of that line, // then indentation does not need to be increased. if ((tokenIndex == 0 || tokens[tokenIndex - 1].Kind == TokenKind.NewLine) && - (tokenIndex < tokens.Length - 1 && NextTokenIgnoringComments(tokens, tokenIndex)?.Kind != TokenKind.NewLine)) + NextTokenIgnoringComments(tokens, tokenIndex)?.Kind != TokenKind.NewLine) { onNewLine = false; lParenSkippedIndentation.Push(true); @@ -294,7 +299,7 @@ public override IEnumerable AnalyzeScript(Ast ast, string file private static Token NextTokenIgnoringComments(Token[] tokens, int startIndex) { - if (startIndex == tokens.Length - 1) + if (startIndex >= tokens.Length - 1) { return null; } diff --git a/test.ps1 b/test.ps1 deleted file mode 100644 index 84452c378..000000000 --- a/test.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -Get-ChildItem -path $path -Get-ChildItem -path $path | Should -Be $exp -Get-ChildItem -path $path | Should -Be $exp -foo|bar # addWhitespaceAroundPipe -foo | bar # trimWhitespaceAroundPipe -foo -barc # whitespaceBetweenParameters -if ($true) {} - -foo | - bar - -foo | - bar - -foo | -bar - -foo | -bar | -baz