diff --git a/Engine/Settings/CodeFormatting.psd1 b/Engine/Settings/CodeFormatting.psd1 index c6c94129c..02a07a1da 100644 --- a/Engine/Settings/CodeFormatting.psd1 +++ b/Engine/Settings/CodeFormatting.psd1 @@ -24,6 +24,7 @@ PSUseConsistentIndentation = @{ Enable = $true + Kind = 'space' IndentationSize = 4 } diff --git a/Engine/Settings/CodeFormattingAllman.psd1 b/Engine/Settings/CodeFormattingAllman.psd1 index bf10dffd8..15b0ecfbd 100644 --- a/Engine/Settings/CodeFormattingAllman.psd1 +++ b/Engine/Settings/CodeFormattingAllman.psd1 @@ -24,6 +24,7 @@ PSUseConsistentIndentation = @{ Enable = $true + Kind = 'space' IndentationSize = 4 } diff --git a/Engine/Settings/CodeFormattingOTBS.psd1 b/Engine/Settings/CodeFormattingOTBS.psd1 index 2bd6d69d4..589487235 100644 --- a/Engine/Settings/CodeFormattingOTBS.psd1 +++ b/Engine/Settings/CodeFormattingOTBS.psd1 @@ -24,6 +24,7 @@ PSUseConsistentIndentation = @{ Enable = $true + Kind = 'space' IndentationSize = 4 } diff --git a/Engine/Settings/CodeFormattingStroustrup.psd1 b/Engine/Settings/CodeFormattingStroustrup.psd1 index 8640f6761..51cbb540a 100644 --- a/Engine/Settings/CodeFormattingStroustrup.psd1 +++ b/Engine/Settings/CodeFormattingStroustrup.psd1 @@ -25,6 +25,7 @@ PSUseConsistentIndentation = @{ Enable = $true + Kind = 'space' IndentationSize = 4 } diff --git a/RuleDocumentation/UseConsistentIndentation.md b/RuleDocumentation/UseConsistentIndentation.md index f89f7324a..51c32ba18 100644 --- a/RuleDocumentation/UseConsistentIndentation.md +++ b/RuleDocumentation/UseConsistentIndentation.md @@ -28,3 +28,10 @@ Enable or disable the rule during ScriptAnalyzer invocation. #### IndentationSize: bool (Default value is `4`) Indentation size in the number of space characters. + +#### Kind: string (Default value is `space`) + +Represents the kind of indentation to be used. Possible values are: `space`, `tab`. If any invalid value is given, the property defaults to `space`. + +`space` means `IndentationSize` number of `space` characters are used to provide one level of indentation. +`tab` means a tab character, `\t`. diff --git a/Rules/UseConsistentIndentation.cs b/Rules/UseConsistentIndentation.cs index b7da9c629..58880d021 100644 --- a/Rules/UseConsistentIndentation.cs +++ b/Rules/UseConsistentIndentation.cs @@ -36,10 +36,48 @@ public class UseConsistentIndentation : ConfigurableRule [ConfigurableRuleProperty(defaultValue: 4)] public int IndentationSize { get; protected set; } - private enum IndentationKind { Space, Tab }; + + // Cannot name to IndentationKind due to the enum type of the same name. + /// + /// Represents the kind of indentation to be used. + /// + /// Possible values are: `space`, `tab`. If any invalid value is given, the + /// property defaults to `space`. + /// + /// `space` means `IndentationSize` number of `space` characters are used to provide one level of indentation. + /// `tab` means a tab character, `\t`. + /// + [ConfigurableRuleProperty(defaultValue: "space")] + public string Kind + { + get + { + return indentationKind.ToString(); + } + set + { + if (String.IsNullOrWhiteSpace(value) || + !Enum.TryParse(value, true, out indentationKind)) + { + indentationKind = IndentationKind.Space; + } + } + } + + private bool insertSpaces; + private char indentationChar; + private int indentationLevelMultiplier; + + // TODO Enable auto when the rule is able to detect indentation + private enum IndentationKind { + Space, + Tab, + // Auto + }; // TODO make this configurable - private readonly IndentationKind indentationKind = IndentationKind.Space; + private IndentationKind indentationKind = IndentationKind.Space; + /// /// Analyzes the given ast to find violations. @@ -61,6 +99,14 @@ public override IEnumerable AnalyzeScript(Ast ast, string file return Enumerable.Empty(); } + // It is more efficient to initialize these fields in ConfigurRule method + // but when the rule will enable `Auto` IndentationKind, we will anyways need to move + // the setting of these variables back here after the rule detects the indentation kind for + // each invocation. + insertSpaces = indentationKind == IndentationKind.Space; + indentationChar = insertSpaces ? ' ' : '\t'; + indentationLevelMultiplier = insertSpaces ? IndentationSize : 1; + var tokens = Helper.Instance.Tokens; var diagnosticRecords = new List(); var indentationLevel = 0; @@ -262,17 +308,13 @@ private int GetIndentationColumnNumber(int indentationLevel) private int GetIndentation(int indentationLevel) { - return indentationLevel * this.IndentationSize; - } - - private char GetIndentationChar() - { - return indentationKind == IndentationKind.Space ? ' ' : '\t'; + // todo if condition can be evaluated during rule configuration + return indentationLevel * indentationLevelMultiplier; } private string GetIndentationString(int indentationLevel) { - return new string(GetIndentationChar(), GetIndentation(indentationLevel)); + return new string(indentationChar, GetIndentation(indentationLevel)); } } } diff --git a/Tests/Rules/UseConsistentIndentation.tests.ps1 b/Tests/Rules/UseConsistentIndentation.tests.ps1 index 3dd083712..89bb6d3f1 100644 --- a/Tests/Rules/UseConsistentIndentation.tests.ps1 +++ b/Tests/Rules/UseConsistentIndentation.tests.ps1 @@ -6,17 +6,19 @@ Import-Module (Join-Path $testRootDirectory "PSScriptAnalyzerTestHelper.psm1") $indentationUnit = ' ' $indentationSize = 4 +$ruleConfiguration = @{ + Enable = $true + IndentationSize = 4 + Kind = 'space' +} + $settings = @{ IncludeRules = @("PSUseConsistentIndentation") - Rules = @{ - PSUseConsistentIndentation = @{ - Enable = $true - IndentationSize = 4 - } + Rules = @{ + PSUseConsistentIndentation = $ruleConfiguration } } - Describe "UseConsistentIndentation" { Context "When top level indentation is not consistent" { BeforeAll { @@ -127,8 +129,8 @@ function foo { get-process | where-object {$_.Name -match 'powershell'} '@ - $violations = Invoke-ScriptAnalyzer -ScriptDefinition $def -Settings $settings - $violations.Count | Should Be 1 + $violations = Invoke-ScriptAnalyzer -ScriptDefinition $def -Settings $settings + $violations.Count | Should Be 1 } It "Should not find a violation if a pipleline element is indented correctly" { @@ -136,8 +138,8 @@ where-object {$_.Name -match 'powershell'} get-process | where-object {$_.Name -match 'powershell'} '@ - $violations = Invoke-ScriptAnalyzer -ScriptDefinition $def -Settings $settings - $violations.Count | Should Be 0 + $violations = Invoke-ScriptAnalyzer -ScriptDefinition $def -Settings $settings + $violations.Count | Should Be 0 } It "Should ignore comment in the pipleline" { @@ -147,8 +149,8 @@ get-process | select Name,Id | format-list '@ - $violations = Invoke-ScriptAnalyzer -ScriptDefinition $def -Settings $settings - $violations.Count | Should Be 3 + $violations = Invoke-ScriptAnalyzer -ScriptDefinition $def -Settings $settings + $violations.Count | Should Be 3 } It "Should indent properly after line continuation (backtick) character" { @@ -159,13 +161,46 @@ $x = "this " + ` $violations = Invoke-ScriptAnalyzer -ScriptDefinition $def -Settings $settings $violations.Count | Should Be 1 $params = @{ - RawContent = $def + RawContent = $def DiagnosticRecord = $violations[0] CorrectionsCount = 1 - ViolationText = "`"Should be indented properly`"" - CorrectionText = (New-Object -TypeName String -ArgumentList $indentationUnit,$indentationSize) + "`"Should be indented properly`"" + ViolationText = "`"Should be indented properly`"" + CorrectionText = (New-Object -TypeName String -ArgumentList $indentationUnit, $indentationSize) + "`"Should be indented properly`"" } Test-CorrectionExtentFromContent @params } } + + Context "When tabs instead of spaces are used for indentation" { + BeforeAll { + $ruleConfiguration.'Kind' = 'tab' + } + + It "Should indent using tabs" { + $def = @' +function foo +{ +get-childitem +$x=1+2 +$hashtable = @{ +property1 = "value" + anotherProperty = "another value" +} +} +'@ + ${t} = "`t" + $expected = @" +function foo +{ +${t}get-childitem +${t}`$x=1+2 +${t}`$hashtable = @{ +${t}${t}property1 = "value" +${t}${t}anotherProperty = "another value" +${t}} +} +"@ + Invoke-Formatter -ScriptDefinition $def -Settings $settings | Should Be $expected + } + } }