Skip to content

Commit c6b7b91

Browse files
Merge pull request #1864 from PowerShell/andschwa/pssa
Use `HostInfo.BundledModulePath` to find PSScriptAnalyzer
2 parents c4f424f + b5e7c77 commit c6b7b91

16 files changed

+120
-70
lines changed

src/PowerShellEditorServices/Hosting/HostStartupInfo.cs

+11-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.IO;
67
using System.Management.Automation.Host;
78
using System.Management.Automation.Runspaces;
89

@@ -92,7 +93,7 @@ public sealed class HostStartupInfo
9293
public string LogPath { get; }
9394

9495
/// <summary>
95-
/// The InitialSessionState will be inherited from the orginal PowerShell process. This will
96+
/// The InitialSessionState will be inherited from the original PowerShell process. This will
9697
/// be used when creating runspaces so that we honor the same InitialSessionState.
9798
/// </summary>
9899
public InitialSessionState InitialSessionState { get; }
@@ -167,7 +168,15 @@ public HostStartupInfo(
167168
LogLevel = logLevel;
168169
ConsoleReplEnabled = consoleReplEnabled;
169170
UsesLegacyReadLine = usesLegacyReadLine;
170-
BundledModulePath = bundledModulePath;
171+
172+
// Respect a user provided bundled module path.
173+
BundledModulePath = Directory.Exists(bundledModulePath)
174+
? bundledModulePath
175+
: Path.GetFullPath(Path.Combine(
176+
Path.GetDirectoryName(typeof(HostStartupInfo).Assembly.Location),
177+
"..",
178+
"..",
179+
".."));
171180
}
172181

173182
#endregion

src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs

+12-15
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Threading;
1212
using System.Threading.Tasks;
1313
using Microsoft.Extensions.Logging;
14+
using Microsoft.PowerShell.EditorServices.Hosting;
1415
using Microsoft.PowerShell.EditorServices.Services.Analysis;
1516
using Microsoft.PowerShell.EditorServices.Services.Configuration;
1617
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
@@ -52,8 +53,7 @@ internal static string GetUniqueIdFromDiagnostic(Diagnostic diagnostic)
5253
.Append(':')
5354
.Append(end.Character);
5455

55-
string id = sb.ToString();
56-
return id;
56+
return sb.ToString();
5757
}
5858

5959
/// <summary>
@@ -96,20 +96,16 @@ internal static string GetUniqueIdFromDiagnostic(Diagnostic diagnostic)
9696

9797
private CancellationTokenSource _diagnosticsCancellationTokenSource;
9898

99+
private readonly string _pssaModulePath;
100+
99101
private string _pssaSettingsFilePath;
100102

101-
/// <summary>
102-
/// Construct a new AnalysisService.
103-
/// </summary>
104-
/// <param name="loggerFactory">Logger factory to create logger instances with.</param>
105-
/// <param name="languageServer">The LSP language server for notifications.</param>
106-
/// <param name="configurationService">The configuration service to query for configurations.</param>
107-
/// <param name="workspaceService">The workspace service for file handling within a workspace.</param>
108103
public AnalysisService(
109104
ILoggerFactory loggerFactory,
110105
ILanguageServerFacade languageServer,
111106
ConfigurationService configurationService,
112-
WorkspaceService workspaceService)
107+
WorkspaceService workspaceService,
108+
HostStartupInfo hostInfo)
113109
{
114110
_loggerFactory = loggerFactory;
115111
_logger = loggerFactory.CreateLogger<AnalysisService>();
@@ -119,6 +115,7 @@ public AnalysisService(
119115
_analysisDelayMillis = 750;
120116
_mostRecentCorrectionsByFile = new ConcurrentDictionary<ScriptFile, CorrectionTableEntry>();
121117
_analysisEngineLazy = new Lazy<PssaCmdletAnalysisEngine>(InstantiateAnalysisEngine);
118+
_pssaModulePath = Path.Combine(hostInfo.BundledModulePath, "PSScriptAnalyzer");
122119
_pssaSettingsFilePath = null;
123120
}
124121

@@ -202,7 +199,7 @@ public async Task<string> GetCommentHelpText(string functionText, string helpLoc
202199
return null;
203200
}
204201

205-
Hashtable commentHelpSettings = AnalysisService.GetCommentHelpRuleSettings(helpLocation, forBlockComment);
202+
Hashtable commentHelpSettings = GetCommentHelpRuleSettings(helpLocation, forBlockComment);
206203

207204
ScriptFileMarker[] analysisResults = await AnalysisEngine.AnalyzeScriptAsync(functionText, commentHelpSettings).ConfigureAwait(false);
208205

@@ -285,7 +282,7 @@ private void InitializeAnalysisEngineToCurrentSettings()
285282
_analysisEngineLazy = new Lazy<PssaCmdletAnalysisEngine>(() => RecreateAnalysisEngine(currentAnalysisEngine));
286283
}
287284

288-
private PssaCmdletAnalysisEngine InstantiateAnalysisEngine()
285+
internal PssaCmdletAnalysisEngine InstantiateAnalysisEngine()
289286
{
290287
PssaCmdletAnalysisEngine.Builder pssaCmdletEngineBuilder = new(_loggerFactory);
291288

@@ -302,7 +299,7 @@ private PssaCmdletAnalysisEngine InstantiateAnalysisEngine()
302299
pssaCmdletEngineBuilder.WithIncludedRules(s_defaultRules);
303300
}
304301

305-
return pssaCmdletEngineBuilder.Build();
302+
return pssaCmdletEngineBuilder.Build(_pssaModulePath);
306303
}
307304

308305
private PssaCmdletAnalysisEngine RecreateAnalysisEngine(PssaCmdletAnalysisEngine oldAnalysisEngine)
@@ -320,15 +317,15 @@ private PssaCmdletAnalysisEngine RecreateAnalysisEngine(PssaCmdletAnalysisEngine
320317

321318
private bool TryFindSettingsFile(out string settingsFilePath)
322319
{
323-
string configuredPath = _configurationService.CurrentSettings.ScriptAnalysis.SettingsPath;
320+
string configuredPath = _configurationService?.CurrentSettings.ScriptAnalysis.SettingsPath;
324321

325322
if (string.IsNullOrEmpty(configuredPath))
326323
{
327324
settingsFilePath = null;
328325
return false;
329326
}
330327

331-
settingsFilePath = _workspaceService.ResolveWorkspacePath(configuredPath);
328+
settingsFilePath = _workspaceService?.ResolveWorkspacePath(configuredPath);
332329

333330
if (settingsFilePath == null
334331
|| !File.Exists(settingsFilePath))

src/PowerShellEditorServices/Services/Analysis/PssaCmdletAnalysisEngine.cs

+8-20
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.Collections;
66
using System.Collections.Generic;
77
using System.Collections.ObjectModel;
8-
using System.IO;
98
using System.Linq;
109
using System.Text;
1110
using System.Threading.Tasks;
@@ -67,35 +66,27 @@ public Builder WithIncludedRules(string[] rules)
6766
/// If PSScriptAnalyzer cannot be found, this will return null.
6867
/// </summary>
6968
/// <returns>A newly configured PssaCmdletAnalysisEngine, or null if PSScriptAnalyzer cannot be found.</returns>
70-
public PssaCmdletAnalysisEngine Build()
69+
public PssaCmdletAnalysisEngine Build(string pssaModulePath)
7170
{
7271
// RunspacePool takes care of queuing commands for us so we do not
7372
// need to worry about executing concurrent commands
7473
ILogger logger = _loggerFactory.CreateLogger<PssaCmdletAnalysisEngine>();
7574
try
7675
{
77-
RunspacePool pssaRunspacePool = CreatePssaRunspacePool();
78-
79-
PssaCmdletAnalysisEngine cmdletAnalysisEngine = _settingsParameter is not null
80-
? new PssaCmdletAnalysisEngine(logger, pssaRunspacePool, _settingsParameter)
81-
: new PssaCmdletAnalysisEngine(logger, pssaRunspacePool, _rules);
82-
76+
logger.LogDebug("Creating PSScriptAnalyzer runspace with module at: '{Path}'", pssaModulePath);
77+
RunspacePool pssaRunspacePool = CreatePssaRunspacePool(pssaModulePath);
78+
PssaCmdletAnalysisEngine cmdletAnalysisEngine = new(logger, pssaRunspacePool, _settingsParameter ?? _rules);
8379
cmdletAnalysisEngine.LogAvailablePssaFeatures();
8480
return cmdletAnalysisEngine;
8581
}
86-
catch (FileNotFoundException e)
82+
catch (Exception ex)
8783
{
88-
logger.LogError(e, $"Unable to find PSScriptAnalyzer. Disabling script analysis. PSModulePath: '{Environment.GetEnvironmentVariable("PSModulePath")}'");
84+
logger.LogError(ex, "Unable to load PSScriptAnalyzer, disabling script analysis!");
8985
return null;
9086
}
9187
}
9288
}
9389

94-
// This is a default that can be overriden at runtime by the user or tests.
95-
// TODO: Deduplicate this logic with PsesInternalHost.
96-
private static readonly string s_pssaModulePath = Path.GetFullPath(Path.Combine(
97-
Path.GetDirectoryName(typeof(PssaCmdletAnalysisEngine).Assembly.Location), "..", "..", "..", "PSScriptAnalyzer"));
98-
9990
/// <summary>
10091
/// The indentation to add when the logger lists errors.
10192
/// </summary>
@@ -365,7 +356,7 @@ private IEnumerable<string> GetPSScriptAnalyzerRules()
365356
/// This looks for the latest version of PSScriptAnalyzer on the path and loads that.
366357
/// </summary>
367358
/// <returns>A runspace pool with PSScriptAnalyzer loaded for running script analysis tasks.</returns>
368-
private static RunspacePool CreatePssaRunspacePool()
359+
private static RunspacePool CreatePssaRunspacePool(string pssaModulePath)
369360
{
370361
using PowerShell pwsh = PowerShell.Create(RunspaceMode.NewRunspace);
371362

@@ -375,10 +366,7 @@ private static RunspacePool CreatePssaRunspacePool()
375366
// We intentionally use `CreateDefault2()` as it loads `Microsoft.PowerShell.Core`
376367
// only, which is a more minimal and therefore safer state.
377368
InitialSessionState sessionState = InitialSessionState.CreateDefault2();
378-
379-
sessionState.ImportPSModulesFromPath(s_pssaModulePath);
380-
// pwsh.ImportModule(s_pssaModulePath);
381-
// sessionState.ImportPSModule(new[] { pssaModuleInfo.ModuleBase });
369+
sessionState.ImportPSModulesFromPath(pssaModulePath);
382370

383371
RunspacePool runspacePool = RunspaceFactory.CreateRunspacePool(sessionState);
384372

src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs

+8-16
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,6 @@ namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Host
3232
internal class PsesInternalHost : PSHost, IHostSupportsInteractiveSession, IRunspaceContext, IInternalPowerShellExecutionService
3333
{
3434
private const string DefaultPrompt = "> ";
35-
// This is a default that can be overriden at runtime by the user or tests.
36-
private static string s_bundledModulePath = Path.GetFullPath(Path.Combine(
37-
Path.GetDirectoryName(typeof(PsesInternalHost).Assembly.Location), "..", "..", ".."));
38-
39-
private static string CommandsModulePath => Path.GetFullPath(Path.Combine(
40-
s_bundledModulePath, "PowerShellEditorServices", "Commands", "PowerShellEditorServices.Commands.psd1"));
4135

4236
private static readonly PropertyInfo s_scriptDebuggerTriggerObjectProperty;
4337

@@ -115,14 +109,6 @@ public PsesInternalHost(
115109
_logger = loggerFactory.CreateLogger<PsesInternalHost>();
116110
_languageServer = languageServer;
117111
_hostInfo = hostInfo;
118-
119-
// Respect a user provided bundled module path.
120-
if (Directory.Exists(hostInfo.BundledModulePath))
121-
{
122-
_logger.LogTrace($"Using new bundled module path: {hostInfo.BundledModulePath}");
123-
s_bundledModulePath = hostInfo.BundledModulePath;
124-
}
125-
126112
_readLineProvider = new ReadLineProvider(loggerFactory);
127113
_taskQueue = new BlockingConcurrentDeque<ISynchronousTask>();
128114
_psFrameStack = new Stack<PowerShellContextFrame>();
@@ -1012,7 +998,13 @@ private static PowerShell CreatePowerShellForRunspace(Runspace runspace)
1012998
pwsh.SetCorrectExecutionPolicy(_logger);
1013999
}
10141000

1015-
pwsh.ImportModule(CommandsModulePath);
1001+
string commandsModulePath = Path.Combine(
1002+
_hostInfo.BundledModulePath,
1003+
"PowerShellEditorServices",
1004+
"Commands",
1005+
"PowerShellEditorServices.Commands.psd1");
1006+
1007+
pwsh.ImportModule(commandsModulePath);
10161008

10171009
if (hostStartupInfo.AdditionalModules?.Count > 0)
10181010
{
@@ -1311,7 +1303,7 @@ internal bool TryLoadPSReadLine(PowerShell pwsh, EngineIntrinsics engineIntrinsi
13111303
psrlReadLine = null;
13121304
try
13131305
{
1314-
PSReadLineProxy psrlProxy = PSReadLineProxy.LoadAndCreate(_loggerFactory, s_bundledModulePath, pwsh);
1306+
PSReadLineProxy psrlProxy = PSReadLineProxy.LoadAndCreate(_loggerFactory, _hostInfo.BundledModulePath, pwsh);
13151307
psrlReadLine = new PsrlReadLine(psrlProxy, this, engineIntrinsics, ReadKey, OnPowerShellIdle);
13161308
return true;
13171309
}

test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
using Microsoft.PowerShell.EditorServices.Services.DebugAdapter;
1616
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host;
1717
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
18+
using Microsoft.PowerShell.EditorServices.Test;
1819
using Microsoft.PowerShell.EditorServices.Test.Shared;
1920
using Microsoft.PowerShell.EditorServices.Utility;
2021
using Xunit;
21-
namespace Microsoft.PowerShell.EditorServices.Test.Debugging
22+
23+
namespace PowerShellEditorServices.Test.Debugging
2224
{
2325
[Trait("Category", "DebugService")]
2426
public class DebugServiceTests : IDisposable

test/PowerShellEditorServices.Test/Extensions/ExtensionCommandTests.cs

+6-5
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313
using Microsoft.PowerShell.EditorServices.Services.Extension;
1414
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host;
1515
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
16+
using Microsoft.PowerShell.EditorServices.Test;
1617
using Microsoft.PowerShell.EditorServices.Test.Shared;
1718
using Xunit;
1819

19-
namespace Microsoft.PowerShell.EditorServices.Test.Extensions
20+
namespace PowerShellEditorServices.Test.Extensions
2021
{
2122
[Trait("Category", "Extensions")]
2223
public class ExtensionCommandTests : IDisposable
@@ -59,7 +60,7 @@ public async Task CanRegisterAndInvokeCommandWithCmdletName()
5960
BufferRange.None);
6061

6162
EditorCommand commandAdded = null;
62-
extensionCommandService.CommandAdded += (object _, EditorCommand command) => commandAdded = command;
63+
extensionCommandService.CommandAdded += (_, command) => commandAdded = command;
6364

6465
const string commandName = "test.function";
6566
const string commandDisplayName = "Function extension";
@@ -95,7 +96,7 @@ public async Task CanRegisterAndInvokeCommandWithScriptBlock()
9596
BufferRange.None);
9697

9798
EditorCommand commandAdded = null;
98-
extensionCommandService.CommandAdded += (object _, EditorCommand command) => commandAdded = command;
99+
extensionCommandService.CommandAdded += (_, command) => commandAdded = command;
99100

100101
const string commandName = "test.scriptblock";
101102
const string commandDisplayName = "ScriptBlock extension";
@@ -126,7 +127,7 @@ await psesHost.ExecutePSCommandAsync(
126127
public async Task CanUpdateRegisteredCommand()
127128
{
128129
EditorCommand updatedCommand = null;
129-
extensionCommandService.CommandUpdated += (object _, EditorCommand command) => updatedCommand = command;
130+
extensionCommandService.CommandUpdated += (_, command) => updatedCommand = command;
130131

131132
const string commandName = "test.function";
132133
const string commandDisplayName = "Updated function extension";
@@ -160,7 +161,7 @@ public async Task CanUnregisterCommand()
160161
const string commandDisplayName = "ScriptBlock extension";
161162

162163
EditorCommand removedCommand = null;
163-
extensionCommandService.CommandRemoved += (object _, EditorCommand command) => removedCommand = command;
164+
extensionCommandService.CommandRemoved += (_, command) => removedCommand = command;
164165

165166
// Add the command and wait for the add event
166167
await psesHost.ExecutePSCommandAsync(

test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@
1111
using Microsoft.PowerShell.EditorServices.Services;
1212
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host;
1313
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
14+
using Microsoft.PowerShell.EditorServices.Test;
1415
using Microsoft.PowerShell.EditorServices.Test.Shared;
1516
using Microsoft.PowerShell.EditorServices.Test.Shared.Completion;
1617
using Microsoft.PowerShell.EditorServices.Utility;
1718
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
1819
using Xunit;
1920

20-
namespace Microsoft.PowerShell.EditorServices.Test.Language
21+
namespace PowerShellEditorServices.Test.Language
2122
{
2223
[Trait("Category", "Completions")]
2324
public class CompletionHandlerTests : IDisposable

test/PowerShellEditorServices.Test/Language/SemanticTokenTest.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
using System.Collections.Generic;
66
using System.IO;
77
using System.Management.Automation.Language;
8-
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
98
using Microsoft.PowerShell.EditorServices.Handlers;
9+
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
1010
using OmniSharp.Extensions.LanguageServer.Protocol;
1111
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
1212
using Xunit;
1313

14-
namespace Microsoft.PowerShell.EditorServices.Test.Language
14+
namespace PowerShellEditorServices.Test.Language
1515
{
1616
public class SemanticTokenTest
1717
{

test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Utility;
1515
using Microsoft.PowerShell.EditorServices.Services.Symbols;
1616
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
17+
using Microsoft.PowerShell.EditorServices.Test;
1718
using Microsoft.PowerShell.EditorServices.Test.Shared;
1819
using Microsoft.PowerShell.EditorServices.Test.Shared.Definition;
1920
using Microsoft.PowerShell.EditorServices.Test.Shared.Occurrences;
@@ -23,7 +24,7 @@
2324
using Microsoft.PowerShell.EditorServices.Test.Shared.Symbols;
2425
using Xunit;
2526

26-
namespace Microsoft.PowerShell.EditorServices.Test.Language
27+
namespace PowerShellEditorServices.Test.Language
2728
{
2829
[Trait("Category", "Symbols")]
2930
public class SymbolsServiceTests : IDisposable

test/PowerShellEditorServices.Test/Language/TokenOperationsTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
99
using Xunit;
1010

11-
namespace Microsoft.PowerShell.EditorServices.Test.Language
11+
namespace PowerShellEditorServices.Test.Language
1212
{
1313
public class TokenOperationsTests
1414
{

test/PowerShellEditorServices.Test/Services/Symbols/AstOperationsTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
99
using Xunit;
1010

11-
namespace Microsoft.PowerShell.EditorServices.Test.Services.Symbols
11+
namespace PowerShellEditorServices.Test.Services.Symbols
1212
{
1313
[Trait("Category", "AstOperations")]
1414
public class AstOperationsTests

0 commit comments

Comments
 (0)