diff --git a/Engine/Commands/InvokeFormatterCommand.cs b/Engine/Commands/InvokeFormatterCommand.cs
new file mode 100644
index 000000000..72aa905a8
--- /dev/null
+++ b/Engine/Commands/InvokeFormatterCommand.cs
@@ -0,0 +1,130 @@
+//
+// Copyright (c) Microsoft Corporation.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+using System;
+using System.Globalization;
+using System.Management.Automation;
+
+namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands
+{
+ using PSSASettings = Microsoft.Windows.PowerShell.ScriptAnalyzer.Settings;
+
+ ///
+ /// A cmdlet to format a PowerShell script text.
+ ///
+ [Cmdlet(VerbsLifecycle.Invoke, "Formatter")]
+ public class InvokeFormatterCommand : PSCmdlet, IOutputWriter
+ {
+ private const string defaultSettingsPreset = "CodeFormatting";
+ private Settings defaultSettings;
+ private Settings inputSettings;
+
+ ///
+ /// The script text to be formated.
+ ///
+ /// *NOTE*: Unlike ScriptBlock parameter, the ScriptDefinition parameter require a string value.
+ ///
+ [ParameterAttribute(Mandatory = true)]
+ [ValidateNotNull]
+ public string ScriptDefinition { get; set; }
+
+ ///
+ /// A settings hashtable or a path to a PowerShell data file (.psd1) file that contains the settings.
+ ///
+ [Parameter(Mandatory = false)]
+ [ValidateNotNull]
+ public object Settings { get; set; }
+
+#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;
+
+ ///
+ /// Attaches to an instance of a .Net debugger
+ ///
+ [Parameter(Mandatory = false)]
+ public SwitchParameter AttachAndDebug
+ {
+ get { return attachAndDebug; }
+ set { attachAndDebug = value; }
+ }
+
+ private bool attachAndDebug = false;
+#endif
+
+ protected override void BeginProcessing()
+ {
+#if DEBUG
+ if (attachAndDebug)
+ {
+ if (System.Diagnostics.Debugger.IsAttached)
+ {
+ System.Diagnostics.Debugger.Break();
+ }
+ else
+ {
+ System.Diagnostics.Debugger.Launch();
+ }
+ }
+#endif
+
+ try
+ {
+ inputSettings = PSSASettings.Create(Settings, null, this);
+ if (inputSettings == null)
+ {
+ inputSettings = new PSSASettings(
+ defaultSettingsPreset,
+ PSSASettings.GetSettingPresetFilePath);
+ }
+ }
+ catch
+ {
+ this.WriteWarning(String.Format(CultureInfo.CurrentCulture, Strings.SettingsNotParsable));
+ return;
+ }
+ }
+
+ 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);
+ }
+
+ private void ValidateInputSettings()
+ {
+ // todo implement this
+ return;
+ }
+ }
+}
diff --git a/Engine/Commands/InvokeScriptAnalyzerCommand.cs b/Engine/Commands/InvokeScriptAnalyzerCommand.cs
index c52776a3a..c230c1071 100644
--- a/Engine/Commands/InvokeScriptAnalyzerCommand.cs
+++ b/Engine/Commands/InvokeScriptAnalyzerCommand.cs
@@ -30,12 +30,14 @@
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands
{
+ using PSSASettings = Microsoft.Windows.PowerShell.ScriptAnalyzer.Settings;
+
///
/// InvokeScriptAnalyzerCommand: Cmdlet to statically check PowerShell scripts.
///
[Cmdlet(VerbsLifecycle.Invoke,
"ScriptAnalyzer",
- DefaultParameterSetName="File",
+ DefaultParameterSetName = "File",
HelpUri = "http://go.microsoft.com/fwlink/?LinkId=525914")]
public class InvokeScriptAnalyzerCommand : PSCmdlet, IOutputWriter
{
@@ -208,6 +210,9 @@ public SwitchParameter SaveDscDependency
#endif // !PSV3
#if DEBUG
+ ///
+ /// Attaches to an instance of a .Net debugger
+ ///
[Parameter(Mandatory = false)]
public SwitchParameter AttachAndDebug
{
@@ -260,64 +265,22 @@ protected override void BeginProcessing()
ProcessPath();
}
- object settingsFound;
- var settingsMode = PowerShell.ScriptAnalyzer.Settings.FindSettingsMode(
- this.settings,
- processedPaths == null || processedPaths.Count == 0 ? null : processedPaths[0],
- out settingsFound);
-
- switch (settingsMode)
- {
- case SettingsMode.Auto:
- this.WriteVerbose(
- String.Format(
- CultureInfo.CurrentCulture,
- Strings.SettingsNotProvided,
- path));
- this.WriteVerbose(
- String.Format(
- CultureInfo.CurrentCulture,
- Strings.SettingsAutoDiscovered,
- (string)settingsFound));
- break;
-
- case SettingsMode.Preset:
- case SettingsMode.File:
- this.WriteVerbose(
- String.Format(
- CultureInfo.CurrentCulture,
- Strings.SettingsUsingFile,
- (string)settingsFound));
- break;
-
- case SettingsMode.Hashtable:
- this.WriteVerbose(
- String.Format(
- CultureInfo.CurrentCulture,
- Strings.SettingsUsingHashtable));
- break;
-
- default: // case SettingsMode.None
- this.WriteVerbose(
- String.Format(
- CultureInfo.CurrentCulture,
- Strings.SettingsCannotFindFile));
- break;
- }
-
- if (settingsMode != SettingsMode.None)
+ try
{
- try
+ var settingsObj = PSSASettings.Create(
+ settings,
+ processedPaths == null || processedPaths.Count == 0 ? null : processedPaths[0],
+ this);
+ if (settingsObj != null)
{
- var settingsObj = new Settings(settingsFound);
ScriptAnalyzer.Instance.UpdateSettings(settingsObj);
}
- catch
- {
- this.WriteWarning(String.Format(CultureInfo.CurrentCulture, Strings.SettingsNotParsable));
- stopProcessing = true;
- return;
- }
+ }
+ catch
+ {
+ this.WriteWarning(String.Format(CultureInfo.CurrentCulture, Strings.SettingsNotParsable));
+ stopProcessing = true;
+ return;
}
ScriptAnalyzer.Instance.Initialize(
diff --git a/Engine/EditableText.cs b/Engine/EditableText.cs
index 12b755f1c..b93dc33dd 100644
--- a/Engine/EditableText.cs
+++ b/Engine/EditableText.cs
@@ -100,7 +100,8 @@ public EditableText ApplyEdit(TextEdit textEdit)
currentLineNumber++;
}
- return new EditableText(String.Join(NewLine, lines));
+ // returning self allows us to chain ApplyEdit calls.
+ return this;
}
// TODO Add a method that takes multiple edits, checks if they are unique and applies them.
diff --git a/Engine/Formatter.cs b/Engine/Formatter.cs
new file mode 100644
index 000000000..098e772a9
--- /dev/null
+++ b/Engine/Formatter.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections;
+using System.Management.Automation;
+
+namespace Microsoft.Windows.PowerShell.ScriptAnalyzer
+{
+ ///
+ /// A class to provide code formatting capability.
+ ///
+ public class Formatter
+ {
+ ///
+ /// Format a powershell script.
+ ///
+ /// A string representing a powershell script.
+ /// Settings to be used for formatting
+ /// The range in which formatting should take place.
+ /// The cmdlet object that calls this method.
+ ///
+ public static string Format(
+ string scriptDefinition,
+ Settings settings,
+ Range range,
+ TCmdlet cmdlet) where TCmdlet : PSCmdlet, IOutputWriter
+ {
+ // todo implement notnull attribute for such a check
+ ValidateNotNull(scriptDefinition, "scriptDefinition");
+ ValidateNotNull(settings, "settings");
+ ValidateNotNull(cmdlet, "cmdlet");
+
+ Helper.Instance = new Helper(cmdlet.SessionState.InvokeCommand, cmdlet);
+ Helper.Instance.Initialize();
+
+ var ruleOrder = new string[]
+ {
+ "PSPlaceCloseBrace",
+ "PSPlaceOpenBrace",
+ "PSUseConsistentWhitespace",
+ "PSUseConsistentIndentation",
+ "PSAlignAssignmentStatement"
+ };
+
+ var text = new EditableText(scriptDefinition);
+ foreach (var rule in ruleOrder)
+ {
+ if (!settings.RuleArguments.ContainsKey(rule))
+ {
+ continue;
+ }
+
+ var currentSettings = GetCurrentSettings(settings, rule);
+ ScriptAnalyzer.Instance.UpdateSettings(currentSettings);
+ ScriptAnalyzer.Instance.Initialize(cmdlet, null, null, null, null, true, false);
+
+ Range updatedRange;
+ text = ScriptAnalyzer.Instance.Fix(text, range, out updatedRange);
+ range = updatedRange;
+ }
+
+ return text.ToString();
+ }
+
+ private static void ValidateNotNull(T obj, string name)
+ {
+ if (obj == null)
+ {
+ throw new ArgumentNullException(name);
+ }
+ }
+
+ private static Settings GetCurrentSettings(Settings settings, string rule)
+ {
+ return new Settings(new Hashtable()
+ {
+ {"IncludeRules", new string[] {rule}},
+ {"Rules", new Hashtable() { { rule, new Hashtable(settings.RuleArguments[rule]) } } }
+ });
+ }
+ }
+}
diff --git a/Engine/PSScriptAnalyzer.psd1 b/Engine/PSScriptAnalyzer.psd1
index fceb75f35..57aab3d82 100644
--- a/Engine/PSScriptAnalyzer.psd1
+++ b/Engine/PSScriptAnalyzer.psd1
@@ -65,7 +65,7 @@ FormatsToProcess = @('ScriptAnalyzer.format.ps1xml')
FunctionsToExport = @()
# Cmdlets to export from this module
-CmdletsToExport = @('Get-ScriptAnalyzerRule','Invoke-ScriptAnalyzer')
+CmdletsToExport = @('Get-ScriptAnalyzerRule', 'Invoke-ScriptAnalyzer', 'Invoke-Formatter')
# Variables to export from this module
VariablesToExport = @()
diff --git a/Engine/PSScriptAnalyzer.psm1 b/Engine/PSScriptAnalyzer.psm1
index 03f4a44e6..78a1bbecf 100644
--- a/Engine/PSScriptAnalyzer.psm1
+++ b/Engine/PSScriptAnalyzer.psm1
@@ -14,8 +14,7 @@ $binaryModuleRoot = $PSModuleRoot
if (($PSVersionTable.Keys -contains "PSEdition") -and ($PSVersionTable.PSEdition -ne 'Desktop')) {
$binaryModuleRoot = Join-Path -Path $PSModuleRoot -ChildPath 'coreclr'
}
-else
-{
+else {
if ($PSVersionTable.PSVersion -lt [Version]'5.0') {
$binaryModuleRoot = Join-Path -Path $PSModuleRoot -ChildPath 'PSv3'
}
@@ -29,9 +28,8 @@ $PSModule.OnRemove = {
Remove-Module -ModuleInfo $binaryModule
}
-if (Get-Command Register-ArgumentCompleter -ErrorAction Ignore)
-{
- Register-ArgumentCompleter -CommandName 'Invoke-ScriptAnalyzer' -ParameterName 'Settings' -ScriptBlock {
+if (Get-Command Register-ArgumentCompleter -ErrorAction Ignore) {
+ $settingPresetCompleter = {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParmeter)
[Microsoft.Windows.PowerShell.ScriptAnalyzer.Settings]::GetSettingPresets() | `
@@ -39,8 +37,14 @@ if (Get-Command Register-ArgumentCompleter -ErrorAction Ignore)
ForEach-Object { New-Object System.Management.Automation.CompletionResult $_ }
}
- Function RuleNameCompleter
- {
+ @('Invoke-ScriptAnalyzer', 'Invoke-Formatter') | ForEach-Object {
+ Register-ArgumentCompleter -CommandName $_ `
+ -ParameterName 'Settings' `
+ -ScriptBlock $settingPresetCompleter
+
+ }
+
+ Function RuleNameCompleter {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParmeter)
Get-ScriptAnalyzerRule *$wordToComplete* | `
@@ -50,4 +54,4 @@ if (Get-Command Register-ArgumentCompleter -ErrorAction Ignore)
Register-ArgumentCompleter -CommandName 'Invoke-ScriptAnalyzer' -ParameterName 'IncludeRule' -ScriptBlock $Function:RuleNameCompleter
Register-ArgumentCompleter -CommandName 'Invoke-ScriptAnalyzer' -ParameterName 'ExcludeRule' -ScriptBlock $Function:RuleNameCompleter
Register-ArgumentCompleter -CommandName 'Get-ScriptAnalyzerRule' -ParameterName 'Name' -ScriptBlock $Function:RuleNameCompleter
-}
\ No newline at end of file
+}
diff --git a/Engine/ScriptAnalyzer.cs b/Engine/ScriptAnalyzer.cs
index 61fdd6ec7..ab60859b5 100644
--- a/Engine/ScriptAnalyzer.cs
+++ b/Engine/ScriptAnalyzer.cs
@@ -1527,6 +1527,128 @@ public IEnumerable AnalyzeScriptDefinition(string scriptDefini
return this.AnalyzeSyntaxTree(scriptAst, scriptTokens, String.Empty);
}
+ ///
+ /// Fix the violations in the given script text.
+ ///
+ /// The script text to be fixed.
+ /// The fixed script text.
+ public string Fix(string scriptDefinition)
+ {
+ if (scriptDefinition == null)
+ {
+ throw new ArgumentNullException(nameof(scriptDefinition));
+ }
+
+ Range updatedRange;
+ return Fix(new EditableText(scriptDefinition), null, out updatedRange).ToString();
+ }
+
+ ///
+ /// Fix the violations in the given script text.
+ ///
+ /// An object of type `EditableText` that encapsulates the script text to be fixed.
+ /// The range in which the fixes are allowed.
+ /// The updated range after the fixes have been applied.
+ /// The same instance of `EditableText` that was passed to the method, but the instance encapsulates the fixed script text. This helps in chaining the Fix method.
+ public EditableText Fix(EditableText text, Range range, out Range updatedRange)
+ {
+ if (text == null)
+ {
+ throw new ArgumentNullException(nameof(text));
+ }
+
+ // todo validate range
+ var isRangeNull = range == null;
+ range = isRangeNull ? null : SnapToEdges(text, range);
+ var previousLineCount = text.Lines.Length;
+ var previousUnusedCorrections = 0;
+ do
+ {
+ var records = AnalyzeScriptDefinition(text.ToString());
+ var corrections = records
+ .Select(r => r.SuggestedCorrections)
+ .Where(sc => sc != null && sc.Any())
+ .Select(sc => sc.First())
+ .Where(sc => isRangeNull || (sc.Start >= range.Start && sc.End <= range.End))
+ .ToList();
+
+ this.outputWriter.WriteVerbose($"Found {corrections.Count} violations.");
+ int unusedCorrections;
+ Fix(text, corrections, out unusedCorrections);
+ this.outputWriter.WriteVerbose($"Fixed {corrections.Count - unusedCorrections} violations.");
+
+ // This is an indication of an infinite loop. There is a small chance of this.
+ // It is better to abort the fixing operation at this point.
+ if (previousUnusedCorrections > 0 && previousUnusedCorrections == unusedCorrections)
+ {
+ this.outputWriter.ThrowTerminatingError(new ErrorRecord(
+ new InvalidOperationException(),
+ "FIX_ERROR",
+ ErrorCategory.InvalidOperation,
+ corrections));
+ }
+
+ previousUnusedCorrections = unusedCorrections;
+
+ // todo add a TextLines.NumLines property because accessing TextLines.Lines is expensive
+ var lineCount = text.Lines.Length;
+ if (!isRangeNull && lineCount != previousLineCount)
+ {
+ range = new Range(
+ range.Start,
+ range.End.Shift(lineCount - previousLineCount, 0));
+ range = SnapToEdges(text, range);
+ }
+
+ previousLineCount = lineCount;
+ } while (previousUnusedCorrections > 0);
+
+ updatedRange = range;
+ return text;
+ }
+
+ private static Range SnapToEdges(EditableText text, Range range)
+ {
+ // todo add TextLines.Validate(range) and TextLines.Validate(position)
+ // todo TextLines.Lines should return IList instead of array because TextLines.Lines is expensive
+ return new Range(
+ range.Start.Line,
+ Math.Min(range.Start.Column, 1),
+ range.End.Line,
+ Math.Max(range.End.Column, text.Lines[range.End.Line - 1].Length));
+ }
+
+ private static IEnumerable GetCorrectionExtentsForFix(
+ IEnumerable correctionExtents)
+ {
+ var ceList = correctionExtents.ToList();
+ ceList.Sort((x, y) =>
+ {
+ return x.StartLineNumber < y.StartLineNumber ?
+ 1 :
+ (x.StartLineNumber == y.StartLineNumber ? 0 : -1);
+ });
+
+ return ceList.GroupBy(ce => ce.StartLineNumber).Select(g => g.First());
+ }
+
+ private static EditableText Fix(
+ EditableText text,
+ IEnumerable correctionExtents,
+ out int unusedCorrections)
+ {
+ var correctionsToUse = GetCorrectionExtentsForFix(correctionExtents);
+ var count = 0;
+ foreach (var correction in correctionsToUse)
+ {
+ count++;
+ text.ApplyEdit(correction);
+ }
+
+ unusedCorrections = correctionExtents.Count() - count;
+ return text;
+ }
+
private void BuildScriptPathList(
string path,
bool searchRecursively,
diff --git a/Engine/ScriptAnalyzerEngine.csproj b/Engine/ScriptAnalyzerEngine.csproj
index 1503e06e1..375bcdf5e 100644
--- a/Engine/ScriptAnalyzerEngine.csproj
+++ b/Engine/ScriptAnalyzerEngine.csproj
@@ -67,9 +67,11 @@
+
+
diff --git a/Engine/Settings.cs b/Engine/Settings.cs
index 539a97fda..83cc12f69 100644
--- a/Engine/Settings.cs
+++ b/Engine/Settings.cs
@@ -23,6 +23,9 @@ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer
{
internal enum SettingsMode { None = 0, Auto, File, Hashtable, Preset };
+ ///
+ /// A class to represent the settings provided to ScriptAnalyzer class.
+ ///
public class Settings
{
private string filePath;
@@ -37,6 +40,11 @@ public class Settings
public IEnumerable Severities { get { return severities; } }
public Dictionary> RuleArguments { get { return ruleArguments; } }
+ ///
+ /// Create a settings object from the input object.
+ ///
+ /// An input object of type Hashtable or string.
+ /// A function that takes in a preset and resolves it to a path.
public Settings(object settings, Func presetResolver)
{
if (settings == null)
@@ -90,6 +98,10 @@ public Settings(object settings, Func presetResolver)
}
}
+ ///
+ /// Create a Settings object from the input object.
+ ///
+ /// An input object of type Hashtable or string.
public Settings(object settings) : this(settings, null)
{
}
@@ -159,6 +171,65 @@ public static string GetSettingPresetFilePath(string settingPreset)
return null;
}
+ ///
+ /// Create a settings object from an input object.
+ ///
+ /// An input object of type Hashtable or string.
+ /// The path in which to search for a settings file.
+ /// An output writer.
+ /// An object of Settings type.
+ public static Settings Create(object settingsObj, string cwd, IOutputWriter outputWriter)
+ {
+ object settingsFound;
+ var settingsMode = FindSettingsMode(settingsObj, cwd, out settingsFound);
+
+ switch (settingsMode)
+ {
+ case SettingsMode.Auto:
+ outputWriter?.WriteVerbose(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ Strings.SettingsNotProvided,
+ ""));
+ outputWriter?.WriteVerbose(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ Strings.SettingsAutoDiscovered,
+ (string)settingsFound));
+ break;
+
+ case SettingsMode.Preset:
+ case SettingsMode.File:
+ outputWriter?.WriteVerbose(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ Strings.SettingsUsingFile,
+ (string)settingsFound));
+ break;
+
+ case SettingsMode.Hashtable:
+ outputWriter?.WriteVerbose(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ Strings.SettingsUsingHashtable));
+ break;
+
+ default: // case SettingsMode.None
+ outputWriter?.WriteVerbose(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ Strings.SettingsCannotFindFile));
+ break;
+ }
+
+ if (settingsMode != SettingsMode.None)
+ {
+ return new Settings(settingsFound);
+ }
+
+ return null;
+ }
+
///
/// Recursively convert hashtable to dictionary
///
diff --git a/Engine/Settings/CodeFormatting.psd1 b/Engine/Settings/CodeFormatting.psd1
index ba5d3a1aa..c6c94129c 100644
--- a/Engine/Settings/CodeFormatting.psd1
+++ b/Engine/Settings/CodeFormatting.psd1
@@ -7,34 +7,37 @@
'PSAlignAssignmentStatement'
)
- Rules = @{
- PSPlaceOpenBrace = @{
- Enable = $true
- OnSameLine = $true
- NewLineAfter = $true
+ Rules = @{
+ PSPlaceOpenBrace = @{
+ Enable = $true
+ OnSameLine = $true
+ NewLineAfter = $true
+ IgnoreOneLineBlock = $true
}
- PSPlaceCloseBrace = @{
- Enable = $true
+ PSPlaceCloseBrace = @{
+ Enable = $true
+ NewLineAfter = $true
+ IgnoreOneLineBlock = $true
+ NoEmptyLineBefore = $false
}
PSUseConsistentIndentation = @{
- Enable = $true
+ Enable = $true
IndentationSize = 4
}
- PSUseConsistentWhitespace = @{
- Enable = $true
+ PSUseConsistentWhitespace = @{
+ Enable = $true
CheckOpenBrace = $true
CheckOpenParen = $true
- CheckOperator = $true
+ CheckOperator = $true
CheckSeparator = $true
}
PSAlignAssignmentStatement = @{
- Enable = $true
+ Enable = $true
CheckHashtable = $true
- CheckDSCConfiguration = $true
}
}
}
diff --git a/Engine/project.json b/Engine/project.json
index a53e3160e..39f7daaa8 100644
--- a/Engine/project.json
+++ b/Engine/project.json
@@ -2,7 +2,7 @@
"name": "Microsoft.Windows.PowerShell.ScriptAnalyzer",
"version": "1.13.0",
"dependencies": {
-"System.Management.Automation": "1.0.0-alpha12"
+"System.Management.Automation": "6.0.0-alpha13"
},
"configurations": {
@@ -45,7 +45,8 @@
"Strings.Designer.Core.cs",
"Commands/GetScriptAnalyzerLoggerCommand.cs"
]
- }
+ },
+ "debugType": "portable"
}
}
}
diff --git a/NuGet.Config b/NuGet.Config
index 1dc373452..d9071f66f 100644
--- a/NuGet.Config
+++ b/NuGet.Config
@@ -1,17 +1,9 @@
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
\ No newline at end of file
+
diff --git a/RuleDocumentation/PlaceOpenBrace.md b/RuleDocumentation/PlaceOpenBrace.md
index d2df3e80f..31949901c 100644
--- a/RuleDocumentation/PlaceOpenBrace.md
+++ b/RuleDocumentation/PlaceOpenBrace.md
@@ -29,7 +29,7 @@ Enable or disable the rule during ScriptAnalyzer invocation.
#### OnSameLine: bool (Default value is `$true`)
-# true
+Enforce open brace to be on the same line as that of its preceding keyword.
#### NewLineAfter: bool (Default value is `$true`)
diff --git a/Rules/project.json b/Rules/project.json
index 6d036aa1d..e5f22f917 100644
--- a/Rules/project.json
+++ b/Rules/project.json
@@ -2,7 +2,7 @@
"name": "Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules",
"version": "1.13.0",
"dependencies": {
- "System.Management.Automation": "1.0.0-alpha12",
+ "System.Management.Automation": "6.0.0-alpha13",
"Engine": "1.13.0",
"Newtonsoft.Json": "9.0.1"
},
@@ -46,7 +46,8 @@
"Strings.Designer.Core.cs",
"UseSingularNouns.cs"
]
- }
+ },
+ "debugType": "portable"
},
"dependencies": {
"Microsoft.NETCore.App": {
diff --git a/Tests/Engine/InvokeFormatter.tests.ps1 b/Tests/Engine/InvokeFormatter.tests.ps1
new file mode 100644
index 000000000..1e2ca8bd1
--- /dev/null
+++ b/Tests/Engine/InvokeFormatter.tests.ps1
@@ -0,0 +1,36 @@
+$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
+$testRootDirectory = Split-Path -Parent $directory
+
+Import-Module PSScriptAnalyzer
+Import-Module (Join-Path $testRootDirectory "PSScriptAnalyzerTestHelper.psm1")
+
+Describe "Invoke-Formatter Cmdlet" {
+ Context "When no settings are given" {
+ It "Should format using default settings" {
+ $def = @'
+function foo
+{
+get-childitem
+$x=1+2
+$hashtable = @{
+property1 = "value"
+ anotherProperty = "another value"
+}
+}
+'@
+ $expected = @'
+function foo {
+ get-childitem
+ $x = 1 + 2
+ $hashtable = @{
+ property1 = "value"
+ anotherProperty = "another value"
+ }
+}
+'@
+
+ Invoke-Formatter -ScriptDefinition $def | Should Be $expected
+ }
+ }
+
+}
diff --git a/docs/markdown/Get-ScriptAnalyzerRule.md b/docs/markdown/Get-ScriptAnalyzerRule.md
index 3f75e04cb..ee3e04267 100644
--- a/docs/markdown/Get-ScriptAnalyzerRule.md
+++ b/docs/markdown/Get-ScriptAnalyzerRule.md
@@ -1,6 +1,5 @@
---
external help file: Microsoft.Windows.PowerShell.ScriptAnalyzer.dll-Help.xml
-online version: http://go.microsoft.com/fwlink/?LinkId=525913
schema: 2.0.0
---
diff --git a/docs/markdown/Invoke-Formatter.md b/docs/markdown/Invoke-Formatter.md
new file mode 100644
index 000000000..3a925616d
--- /dev/null
+++ b/docs/markdown/Invoke-Formatter.md
@@ -0,0 +1,100 @@
+---
+external help file: Microsoft.Windows.PowerShell.ScriptAnalyzer.dll-Help.xml
+schema: 2.0.0
+---
+
+# Invoke-Formatter
+
+## SYNOPSIS
+Formats a script text based on the input settings or default settings.
+
+## SYNTAX
+
+```
+Invoke-Formatter [-ScriptDefinition] [-Settings