diff --git a/NuGet.Config b/NuGet.Config
index 2f64451ed..45dd090e9 100644
--- a/NuGet.Config
+++ b/NuGet.Config
@@ -6,5 +6,6 @@
+
diff --git a/module/PowerShellEditorServices/PowerShellEditorServices.psm1 b/module/PowerShellEditorServices/PowerShellEditorServices.psm1
index 770e516f8..66f8878a9 100644
--- a/module/PowerShellEditorServices/PowerShellEditorServices.psm1
+++ b/module/PowerShellEditorServices/PowerShellEditorServices.psm1
@@ -92,6 +92,9 @@ function Start-EditorServicesHost {
$WaitForDebugger
)
+ # Make sure PSScriptAnalyzer dlls are loaded.
+ Import-Module PSScriptAnalyzer
+
$editorServicesHost = $null
$hostDetails =
Microsoft.PowerShell.Utility\New-Object Microsoft.PowerShell.EditorServices.Hosting.HostDetails @(
diff --git a/src/PowerShellEditorServices/PowerShellEditorServices.csproj b/src/PowerShellEditorServices/PowerShellEditorServices.csproj
index 843ee3d7f..17c60b711 100644
--- a/src/PowerShellEditorServices/PowerShellEditorServices.csproj
+++ b/src/PowerShellEditorServices/PowerShellEditorServices.csproj
@@ -31,5 +31,6 @@
+
diff --git a/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs b/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs
index 074178567..4542b8597 100644
--- a/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs
+++ b/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs
@@ -55,14 +55,7 @@ public static IServiceCollection AddPsesLanguageServices (
.Wait();
return extensionService;
})
- .AddSingleton(
- (provider) =>
- {
- return AnalysisService.Create(
- provider.GetService(),
- provider.GetService(),
- provider.GetService().CreateLogger());
- });
+ .AddSingleton();
}
public static IServiceCollection AddPsesDebugServices(
diff --git a/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs b/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs
index a44e73427..b27ee9cfa 100644
--- a/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs
+++ b/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs
@@ -6,17 +6,19 @@
using System;
using System.Linq;
using System.Threading.Tasks;
-using System.Management.Automation.Runspaces;
-using System.Management.Automation;
using System.Collections.Generic;
using System.Text;
-using System.Collections;
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
using System.Threading;
using System.Collections.Concurrent;
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
+using Microsoft.Windows.PowerShell.ScriptAnalyzer.Hosting;
+using Microsoft.Windows.PowerShell.ScriptAnalyzer;
+using Microsoft.PowerShell.EditorServices.Utility;
+using Microsoft.PowerShell.EditorServices.Services.Analysis;
+using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic;
namespace Microsoft.PowerShell.EditorServices.Services
{
@@ -26,7 +28,7 @@ namespace Microsoft.PowerShell.EditorServices.Services
///
public class AnalysisService : IDisposable
{
- #region Static fields
+ #region Fields
///
/// Defines the list of Script Analyzer rules to include by default if
@@ -50,250 +52,88 @@ public class AnalysisService : IDisposable
"PSPossibleIncorrectUsageOfRedirectionOperator"
};
- ///
- /// An empty diagnostic result to return when a script fails analysis.
- ///
- private static readonly PSObject[] s_emptyDiagnosticResult = new PSObject[0];
-
- private static readonly string[] s_emptyGetRuleResult = new string[0];
-
- private static CancellationTokenSource s_existingRequestCancellation;
-
- ///
- /// The indentation to add when the logger lists errors.
- ///
- private static readonly string s_indentJoin = Environment.NewLine + " ";
-
- #endregion // Static fields
-
- #region Private Fields
-
- ///
- /// Maximum number of runspaces we allow to be in use for script analysis.
- ///
- private const int NumRunspaces = 1;
-
- ///
- /// Name of the PSScriptAnalyzer module, to be used for PowerShell module interactions.
- ///
- private const string PSSA_MODULE_NAME = "PSScriptAnalyzer";
-
- ///
- /// Provides logging.
- ///
- private ILogger _logger;
-
- ///
- /// Runspace pool to generate runspaces for script analysis and handle
- /// ansynchronous analysis requests.
- ///
- private RunspacePool _analysisRunspacePool;
-
- ///
- /// Info object describing the PSScriptAnalyzer module that has been loaded in
- /// to provide analysis services.
- ///
- private PSModuleInfo _pssaModuleInfo;
-
+ private readonly ILogger _logger;
+ private readonly HostedAnalyzer _analyzer;
+ private readonly Settings _analyzerSettings;
private readonly ILanguageServer _languageServer;
-
private readonly ConfigurationService _configurationService;
-
private readonly ConcurrentDictionary)> _mostRecentCorrectionsByFile;
- #endregion // Private Fields
+ private CancellationTokenSource _existingRequestCancellation;
+ private readonly SemaphoreSlim _existingRequestCancellationLock;
- #region Properties
+ #endregion
- ///
- /// Set of PSScriptAnalyzer rules used for analysis.
- ///
- public string[] ActiveRules { get; set; }
+ #region Properties
///
/// Gets or sets the path to a settings file (.psd1)
/// containing PSScriptAnalyzer settings.
///
- public string SettingsPath { get; set; }
+ public string SettingsPath { get; internal set; }
#endregion
#region Constructors
- ///
- /// Construct a new AnalysisService object.
- ///
- ///
- /// The runspace pool with PSScriptAnalyzer module loaded that will handle
- /// analysis tasks.
- ///
- ///
- /// The path to the PSScriptAnalyzer settings file to handle analysis settings.
- ///
- /// An array of rules to be used for analysis.
- /// Maintains logs for the analysis service.
- ///
- /// Optional module info of the loaded PSScriptAnalyzer module. If not provided,
- /// the analysis service will populate it, but it can be given here to save time.
- ///
- private AnalysisService(
- RunspacePool analysisRunspacePool,
- string pssaSettingsPath,
- IEnumerable activeRules,
- ILanguageServer languageServer,
- ConfigurationService configurationService,
- ILogger logger,
- PSModuleInfo pssaModuleInfo = null)
+ public AnalysisService(ConfigurationService configurationService, ILanguageServer languageServer, ILoggerFactory factory)
{
- _analysisRunspacePool = analysisRunspacePool;
- SettingsPath = pssaSettingsPath;
- ActiveRules = activeRules.ToArray();
- _languageServer = languageServer;
+ SettingsPath = configurationService.CurrentSettings.ScriptAnalysis.SettingsPath;
+ _logger = factory.CreateLogger();
+ _analyzer = new HostedAnalyzer();
+ _analyzerSettings = _analyzer.CreateSettings(s_includedRules);
+ _analyzerSettings.Severities.AddRange(new [] {
+ RuleSeverity.Error.ToString(),
+ RuleSeverity.Information.ToString(),
+ RuleSeverity.Information.ToString()
+ });
_configurationService = configurationService;
- _logger = logger;
- _pssaModuleInfo = pssaModuleInfo;
+ _languageServer = languageServer;
_mostRecentCorrectionsByFile = new ConcurrentDictionary)>();
+ _existingRequestCancellation = new CancellationTokenSource();
+ _existingRequestCancellationLock = AsyncUtils.CreateSimpleLockingSemaphore();
}
- #endregion // constructors
+ #endregion
#region Public Methods
///
- /// Factory method for producing AnalysisService instances. Handles loading of the PSScriptAnalyzer module
- /// and runspace pool instantiation before creating the service instance.
+ /// Clean up resources.
///
- /// Path to the PSSA settings file to be used for this service instance.
- /// EditorServices logger for logging information.
- ///
- /// A new analysis service instance with a freshly imported PSScriptAnalyzer module and runspace pool.
- /// Returns null if problems occur. This method should never throw.
- ///
- public static AnalysisService Create(ConfigurationService configurationService, ILanguageServer languageServer, ILogger logger)
+ public void Dispose()
{
- string settingsPath = configurationService.CurrentSettings.ScriptAnalysis.SettingsPath;
- try
- {
- RunspacePool analysisRunspacePool;
- PSModuleInfo pssaModuleInfo;
- try
- {
- // Try and load a PSScriptAnalyzer module with the required version
- // by looking on the script path. Deep down, this internally runs Get-Module -ListAvailable,
- // so we'll use this to check whether such a module exists
- analysisRunspacePool = CreatePssaRunspacePool(out pssaModuleInfo);
-
- }
- catch (Exception e)
- {
- throw new AnalysisServiceLoadException("PSScriptAnalyzer runspace pool could not be created", e);
- }
-
- if (analysisRunspacePool == null)
- {
- throw new AnalysisServiceLoadException("PSScriptAnalyzer runspace pool failed to be created");
- }
-
- // Having more than one runspace doesn't block code formatting if one
- // runspace is occupied for diagnostics
- analysisRunspacePool.SetMaxRunspaces(NumRunspaces);
- analysisRunspacePool.ThreadOptions = PSThreadOptions.ReuseThread;
- analysisRunspacePool.Open();
-
- var analysisService = new AnalysisService(
- analysisRunspacePool,
- settingsPath,
- s_includedRules,
- languageServer,
- configurationService,
- logger,
- pssaModuleInfo);
-
- // Log what features are available in PSSA here
- analysisService.LogAvailablePssaFeatures();
-
- return analysisService;
- }
- catch (AnalysisServiceLoadException e)
- {
- logger.LogWarning("PSScriptAnalyzer cannot be imported, AnalysisService will be disabled", e);
- return null;
- }
- catch (Exception e)
- {
- logger.LogWarning("AnalysisService could not be started due to an unexpected exception", e);
- return null;
- }
+ _existingRequestCancellation.Dispose();
+ _analyzer.Dispose();
+ _existingRequestCancellationLock.Dispose();
}
///
- /// Get PSScriptAnalyzer settings hashtable for PSProvideCommentHelp rule.
+ /// Get PSScriptAnalyzer settings for PSProvideCommentHelp rule.
///
/// Enable the rule.
/// Analyze only exported functions/cmdlets.
/// Use block comment or line comment.
/// Return a vscode snipped correction should be returned.
/// Place comment help at the given location relative to the function definition.
- /// A PSScriptAnalyzer settings hashtable.
- public static Hashtable GetCommentHelpRuleSettings(
+ /// A PSScriptAnalyzer settings.
+ public Settings GetCommentHelpRuleSettings(
bool enable,
bool exportedOnly,
bool blockComment,
bool vscodeSnippetCorrection,
string placement)
{
- var settings = new Dictionary();
- var ruleSettings = new Hashtable();
- ruleSettings.Add("Enable", enable);
- ruleSettings.Add("ExportedOnly", exportedOnly);
- ruleSettings.Add("BlockComment", blockComment);
- ruleSettings.Add("VSCodeSnippetCorrection", vscodeSnippetCorrection);
- ruleSettings.Add("Placement", placement);
- settings.Add("PSProvideCommentHelp", ruleSettings);
- return GetPSSASettingsHashtable(settings);
- }
-
- ///
- /// Construct a PSScriptAnalyzer settings hashtable
- ///
- /// A settings hashtable
- ///
- public static Hashtable GetPSSASettingsHashtable(IDictionary ruleSettingsMap)
- {
- var hashtable = new Hashtable();
- var ruleSettingsHashtable = new Hashtable();
-
- hashtable["IncludeRules"] = ruleSettingsMap.Keys.ToArray