diff --git a/Engine/Commands/InvokeFormatterCommand.cs b/Engine/Commands/InvokeFormatterCommand.cs index 4b8278ca3..5d9723df0 100644 --- a/Engine/Commands/InvokeFormatterCommand.cs +++ b/Engine/Commands/InvokeFormatterCommand.cs @@ -12,6 +12,7 @@ using System; using System.Globalization; +using System.Linq; using System.Management.Automation; namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands @@ -26,6 +27,7 @@ public class InvokeFormatterCommand : PSCmdlet, IOutputWriter { private const string defaultSettingsPreset = "CodeFormatting"; private Settings inputSettings; + private Range range; /// /// The script text to be formated. @@ -43,19 +45,20 @@ public class InvokeFormatterCommand : PSCmdlet, IOutputWriter [ValidateNotNull] public object Settings { get; set; } = defaultSettingsPreset; -#if DEBUG - [Parameter(Mandatory = false)] - public Range Range { get; set; } - - [Parameter(Mandatory = false, ParameterSetName = "NoRange")] - public int StartLineNumber { get; set; } = -1; - [Parameter(Mandatory = false, ParameterSetName = "NoRange")] - public int StartColumnNumber { get; set; } = -1; - [Parameter(Mandatory = false, ParameterSetName = "NoRange")] - public int EndLineNumber { get; set; } = -1; - [Parameter(Mandatory = false, ParameterSetName = "NoRange")] - public int EndColumnNumber { get; set; } = -1; + /// + /// The range within which formatting should take place. + /// + /// The parameter is an array of integers of length 4 such that the first, second, third and last + /// elements correspond to the start line number, start column number, end line number and + /// end column number. These numbers must be greater than 0. + /// + /// + [Parameter(Mandatory = false, Position = 3)] + [ValidateNotNull] + [ValidateCount(4, 4)] + public int[] Range { get; set; } +#if DEBUG /// /// Attaches to an instance of a .Net debugger /// @@ -85,6 +88,7 @@ protected override void BeginProcessing() } #endif + this.range = Range == null ? null : new Range(Range[0], Range[1], Range[2], Range[3]); try { inputSettings = PSSASettings.Create(Settings, this.MyInvocation.PSScriptRoot, this); @@ -93,7 +97,7 @@ protected override void BeginProcessing() { this.ThrowTerminatingError(new ErrorRecord( e, - "SETTNGS_ERROR", + "SETTINGS_ERROR", ErrorCategory.InvalidData, Settings)); } @@ -114,17 +118,7 @@ protected override void ProcessRecord() { // todo add tests to check range formatting string formattedScriptDefinition; -#if DEBUG - var range = Range; - if (this.ParameterSetName.Equals("NoRange")) - { - range = new Range(StartLineNumber, StartColumnNumber, EndLineNumber, EndColumnNumber); - } - formattedScriptDefinition = Formatter.Format(ScriptDefinition, inputSettings, range, this); -#endif // DEBUG - - formattedScriptDefinition = Formatter.Format(ScriptDefinition, inputSettings, null, this); this.WriteObject(formattedScriptDefinition); } diff --git a/Engine/EditableText.cs b/Engine/EditableText.cs index b93dc33dd..7aae0c28a 100644 --- a/Engine/EditableText.cs +++ b/Engine/EditableText.cs @@ -106,6 +106,27 @@ public EditableText ApplyEdit(TextEdit textEdit) // TODO Add a method that takes multiple edits, checks if they are unique and applies them. + /// + /// Checks if the range falls within the bounds of the text. + /// + /// + /// + public bool IsValidRange(Range range) + { + if (range == null) + { + throw new ArgumentNullException(nameof(range)); + } + + return range.Start.Line <= Lines.Length + && range.End.Line <= Lines.Length + && range.Start.Column <= Lines[range.Start.Line - 1].Length + && range.End.Column <= Lines[range.End.Line - 1].Length + 1; + } + + /// + /// Returns the text representation of the object. + /// public override string ToString() { return Text; @@ -123,10 +144,7 @@ private void ValidateTextEdit(TextEdit textEdit) private void ValidateTextEditExtent(TextEdit textEdit) { - if (textEdit.StartLineNumber > Lines.Length - || textEdit.EndLineNumber > Lines.Length - || textEdit.StartColumnNumber > Lines[textEdit.StartLineNumber - 1].Length - || textEdit.EndColumnNumber > Lines[textEdit.EndLineNumber - 1].Length + 1) + if (!IsValidRange(textEdit)) { throw new ArgumentException(String.Format( CultureInfo.CurrentCulture, diff --git a/Engine/ScriptAnalyzer.cs b/Engine/ScriptAnalyzer.cs index ab60859b5..919ba8df0 100644 --- a/Engine/ScriptAnalyzer.cs +++ b/Engine/ScriptAnalyzer.cs @@ -1559,6 +1559,15 @@ public EditableText Fix(EditableText text, Range range, out Range updatedRange) // todo validate range var isRangeNull = range == null; + if (!isRangeNull && !text.IsValidRange(range)) + { + this.outputWriter.ThrowTerminatingError(new ErrorRecord( + new ArgumentException("Invalid Range", nameof(range)), + "FIX_ERROR", + ErrorCategory.InvalidArgument, + range)); + } + range = isRangeNull ? null : SnapToEdges(text, range); var previousLineCount = text.Lines.Length; var previousUnusedCorrections = 0; diff --git a/Tests/Engine/InvokeFormatter.tests.ps1 b/Tests/Engine/InvokeFormatter.tests.ps1 index 56764047d..17b25a598 100644 --- a/Tests/Engine/InvokeFormatter.tests.ps1 +++ b/Tests/Engine/InvokeFormatter.tests.ps1 @@ -31,6 +31,27 @@ function foo { Invoke-Formatter $def $settings | Should Be $expected } } + + Context "When a range is given" { + It "Should format only within the range when a range list is given" { + $def = @" +function foo { +"xyz" +"abc" +} +"@ + + $expected = @" +function foo { +"xyz" + "abc" +} +"@ + + Invoke-Formatter -ScriptDefinition $def -Range @(3, 1, 4, 1) | Should Be $expected + } + } + Context "When no settings are given" { It "Should format using default settings" { $def = @' diff --git a/docs/markdown/Invoke-Formatter.md b/docs/markdown/Invoke-Formatter.md index e68a4adba..3eb42b1d8 100644 --- a/docs/markdown/Invoke-Formatter.md +++ b/docs/markdown/Invoke-Formatter.md @@ -10,7 +10,7 @@ Formats a script text based on the input settings or default settings. ## SYNTAX ``` -Invoke-Formatter [-ScriptDefinition] [-Settings ] +Invoke-Formatter [-ScriptDefinition] [-Settings ] [-Range ] ``` ## DESCRIPTION @@ -97,3 +97,17 @@ Default value: CodeFormatting Accept pipeline input: False Accept wildcard characters: False ``` + +### -Range +The range within which formatting should take place. The parameter is an array of integers of length 4 such that the first, second, third and last elements correspond to the start line number, start column number, end line number and end column number. These numbers must be greater than 0. + +```yaml +Type: Int32[] +Parameter Sets: (All) + +Required: False +Position: 3 +Default value: +Accept pipeline input: False +Accept wildcard characters: False +```