31
31
using System . Collections . ObjectModel ;
32
32
using System . Collections ;
33
33
using System . Diagnostics ;
34
+ using System . Text ;
34
35
35
36
namespace Microsoft . Windows . PowerShell . ScriptAnalyzer
36
37
{
@@ -1443,43 +1444,67 @@ public Dictionary<string, List<string>> CheckRuleExtension(string[] path, PathIn
1443
1444
return results ;
1444
1445
}
1445
1446
1446
- #endregion
1447
+ #endregion
1447
1448
1448
1449
1449
1450
/// <summary>
1450
1451
/// Analyzes a script file or a directory containing script files.
1451
1452
/// </summary>
1452
1453
/// <param name="path">The path of the file or directory to analyze.</param>
1454
+ /// <param name="shouldProcess">Whether the action should be executed.</param>
1453
1455
/// <param name="searchRecursively">
1454
1456
/// If true, recursively searches the given file path and analyzes any
1455
1457
/// script files that are found.
1456
1458
/// </param>
1457
1459
/// <returns>An enumeration of DiagnosticRecords that were found by rules.</returns>
1458
- public IEnumerable < DiagnosticRecord > AnalyzePath ( string path , bool searchRecursively = false )
1460
+ public IEnumerable < DiagnosticRecord > AnalyzePath ( string path , Func < string , string , bool > shouldProcess , bool searchRecursively = false )
1459
1461
{
1460
- List < string > scriptFilePaths = new List < string > ( ) ;
1462
+ List < string > scriptFilePaths = ScriptPathList ( path , searchRecursively ) ;
1461
1463
1462
- if ( path == null )
1464
+ foreach ( string scriptFilePath in scriptFilePaths )
1463
1465
{
1464
- this . outputWriter . ThrowTerminatingError (
1465
- new ErrorRecord (
1466
- new FileNotFoundException ( ) ,
1467
- string . Format ( CultureInfo . CurrentCulture , Strings . FileNotFound , path ) ,
1468
- ErrorCategory . InvalidArgument ,
1469
- this ) ) ;
1466
+ if ( shouldProcess ( scriptFilePath , $ "Analyzing file { scriptFilePath } ") )
1467
+ {
1468
+ // Yield each record in the result so that the caller can pull them one at a time
1469
+ foreach ( var diagnosticRecord in this . AnalyzeFile ( scriptFilePath ) )
1470
+ {
1471
+ yield return diagnosticRecord ;
1472
+ }
1473
+ }
1470
1474
}
1475
+ }
1476
+
1477
+ /// <summary>
1478
+ /// Analyzes a script file or a directory containing script files and fixes warning where possible.
1479
+ /// </summary>
1480
+ /// <param name="path">The path of the file or directory to analyze.</param>
1481
+ /// <param name="shouldProcess">Whether the action should be executed.</param>
1482
+ /// <param name="searchRecursively">
1483
+ /// If true, recursively searches the given file path and analyzes any
1484
+ /// script files that are found.
1485
+ /// </param>
1486
+ /// <returns>An enumeration of DiagnosticRecords that were found by rules and could not be fixed automatically.</returns>
1487
+ public IEnumerable < DiagnosticRecord > AnalyzeAndFixPath ( string path , Func < string , string , bool > shouldProcess , bool searchRecursively = false )
1488
+ {
1489
+ List < string > scriptFilePaths = ScriptPathList ( path , searchRecursively ) ;
1471
1490
1472
- // Create in advance the list of script file paths to analyze. This
1473
- // is an optimization over doing the whole operation at once
1474
- // and calling .Concat on IEnumerables to join results.
1475
- this . BuildScriptPathList ( path , searchRecursively , scriptFilePaths ) ;
1476
1491
foreach ( string scriptFilePath in scriptFilePaths )
1477
1492
{
1478
- // Yield each record in the result so that the
1479
- // caller can pull them one at a time
1480
- foreach ( var diagnosticRecord in this . AnalyzeFile ( scriptFilePath ) )
1493
+ if ( shouldProcess ( scriptFilePath , $ "Analyzing and fixing file { scriptFilePath } ") )
1481
1494
{
1482
- yield return diagnosticRecord ;
1495
+ var fileEncoding = GetFileEncoding ( scriptFilePath ) ;
1496
+ bool fixesWereApplied ;
1497
+ var scriptFileContentWithFixes = Fix ( File . ReadAllText ( scriptFilePath , fileEncoding ) , out fixesWereApplied ) ;
1498
+ if ( fixesWereApplied )
1499
+ {
1500
+ File . WriteAllText ( scriptFilePath , scriptFileContentWithFixes , fileEncoding ) ;
1501
+ }
1502
+
1503
+ // Yield each record in the result so that the caller can pull them one at a time
1504
+ foreach ( var diagnosticRecord in this . AnalyzeFile ( scriptFilePath ) )
1505
+ {
1506
+ yield return diagnosticRecord ;
1507
+ }
1483
1508
}
1484
1509
}
1485
1510
}
@@ -1531,16 +1556,17 @@ public IEnumerable<DiagnosticRecord> AnalyzeScriptDefinition(string scriptDefini
1531
1556
/// Fix the violations in the given script text.
1532
1557
/// </summary>
1533
1558
/// <param name="scriptDefinition">The script text to be fixed.</param>
1559
+ /// <param name="updatedRange">Whether any warnings were fixed.</param>
1534
1560
/// <returns>The fixed script text.</returns>
1535
- public string Fix ( string scriptDefinition )
1561
+ public string Fix ( string scriptDefinition , out bool fixesWereApplied )
1536
1562
{
1537
1563
if ( scriptDefinition == null )
1538
1564
{
1539
1565
throw new ArgumentNullException ( nameof ( scriptDefinition ) ) ;
1540
1566
}
1541
1567
1542
1568
Range updatedRange ;
1543
- return Fix ( new EditableText ( scriptDefinition ) , null , out updatedRange ) . ToString ( ) ;
1569
+ return Fix ( new EditableText ( scriptDefinition ) , null , out updatedRange , out fixesWereApplied ) . ToString ( ) ;
1544
1570
}
1545
1571
1546
1572
/// <summary>
@@ -1549,8 +1575,9 @@ public string Fix(string scriptDefinition)
1549
1575
/// <param name="text">An object of type `EditableText` that encapsulates the script text to be fixed.</param>
1550
1576
/// <param name="range">The range in which the fixes are allowed.</param>
1551
1577
/// <param name="updatedRange">The updated range after the fixes have been applied.</param>
1578
+ /// <param name="updatedRange">Whether any warnings were fixed.</param>
1552
1579
/// <returns>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.</returns>
1553
- public EditableText Fix ( EditableText text , Range range , out Range updatedRange )
1580
+ public EditableText Fix ( EditableText text , Range range , out Range updatedRange , out bool fixesWereApplied )
1554
1581
{
1555
1582
if ( text == null )
1556
1583
{
@@ -1583,7 +1610,9 @@ public EditableText Fix(EditableText text, Range range, out Range updatedRange)
1583
1610
this . outputWriter . WriteVerbose ( $ "Found { corrections . Count } violations.") ;
1584
1611
int unusedCorrections ;
1585
1612
Fix ( text , corrections , out unusedCorrections ) ;
1586
- this . outputWriter . WriteVerbose ( $ "Fixed { corrections . Count - unusedCorrections } violations.") ;
1613
+ var numberOfFixedViolatons = corrections . Count - unusedCorrections ;
1614
+ fixesWereApplied = numberOfFixedViolatons > 0 ;
1615
+ this . outputWriter . WriteVerbose ( $ "Fixed { numberOfFixedViolatons } violations.") ;
1587
1616
1588
1617
// This is an indication of an infinite loop. There is a small chance of this.
1589
1618
// It is better to abort the fixing operation at this point.
@@ -1613,6 +1642,18 @@ public EditableText Fix(EditableText text, Range range, out Range updatedRange)
1613
1642
return text ;
1614
1643
}
1615
1644
1645
+ private static Encoding GetFileEncoding ( string path )
1646
+ {
1647
+ using ( var stream = new FileStream ( path , FileMode . Open ) )
1648
+ {
1649
+ using ( var reader = new StreamReader ( stream ) )
1650
+ {
1651
+ reader . Peek ( ) ; // needed in order to populate the CurrentEncoding property
1652
+ return reader . CurrentEncoding ;
1653
+ }
1654
+ }
1655
+ }
1656
+
1616
1657
private static Range SnapToEdges ( EditableText text , Range range )
1617
1658
{
1618
1659
// todo add TextLines.Validate(range) and TextLines.Validate(position)
@@ -1655,6 +1696,28 @@ private static EditableText Fix(
1655
1696
return text ;
1656
1697
}
1657
1698
1699
+ private List < string > ScriptPathList ( string path , bool searchRecursively )
1700
+ {
1701
+ List < string > scriptFilePaths = new List < string > ( ) ;
1702
+
1703
+ if ( path == null )
1704
+ {
1705
+ this . outputWriter . ThrowTerminatingError (
1706
+ new ErrorRecord (
1707
+ new FileNotFoundException ( ) ,
1708
+ string . Format ( CultureInfo . CurrentCulture , Strings . FileNotFound , path ) ,
1709
+ ErrorCategory . InvalidArgument ,
1710
+ this ) ) ;
1711
+ }
1712
+
1713
+ // Create in advance the list of script file paths to analyze. This
1714
+ // is an optimization over doing the whole operation at once
1715
+ // and calling .Concat on IEnumerables to join results.
1716
+ this . BuildScriptPathList ( path , searchRecursively , scriptFilePaths ) ;
1717
+
1718
+ return scriptFilePaths ;
1719
+ }
1720
+
1658
1721
private void BuildScriptPathList (
1659
1722
string path ,
1660
1723
bool searchRecursively ,
0 commit comments