Skip to content

Commit 5a3d905

Browse files
committed
Add support for loading host profile scripts
This change adds support for loading both host-agnostic and host-specific profile scripts for the current user and all users depending on which of those scripts are available on the system. Profile loading behavior is opt-in and not enabled by default. Regardless of whether profiles get loaded, a $profile variable is inserted into the PowerShellContext's runspace upon initialization. Resolves #111.
1 parent 9130e75 commit 5a3d905

File tree

16 files changed

+590
-46
lines changed

16 files changed

+590
-46
lines changed

src/PowerShellEditorServices.Channel.WebSocket/WebsocketServerChannel.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public class LanguageServerWebSocketConnection : EditorServiceWebSocketConnectio
139139
{
140140
public LanguageServerWebSocketConnection()
141141
{
142-
Server = new LanguageServer(Channel);
142+
Server = new LanguageServer(null, Channel);
143143
}
144144
}
145145

@@ -150,7 +150,7 @@ public class DebugAdapterWebSocketConnection : EditorServiceWebSocketConnection
150150
{
151151
public DebugAdapterWebSocketConnection()
152152
{
153-
Server = new DebugAdapter(Channel);
153+
Server = new DebugAdapter(null, Channel);
154154
}
155155
}
156156

src/PowerShellEditorServices.Host/Program.cs

+65-12
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55

66
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
77
using Microsoft.PowerShell.EditorServices.Protocol.Server;
8+
using Microsoft.PowerShell.EditorServices.Session;
89
using Microsoft.PowerShell.EditorServices.Utility;
910
using System;
1011
using System.Diagnostics;
1112
using System.Linq;
12-
using System.Threading;
1313

1414
namespace Microsoft.PowerShell.EditorServices.Host
1515
{
@@ -78,21 +78,63 @@ static void Main(string[] args)
7878
"/debugAdapter",
7979
StringComparison.InvariantCultureIgnoreCase));
8080

81-
// Catch unhandled exceptions for logging purposes
82-
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
81+
string hostProfileId = null;
82+
string hostProfileIdArgument =
83+
args.FirstOrDefault(
84+
arg =>
85+
arg.StartsWith(
86+
"/hostProfileId:",
87+
StringComparison.InvariantCultureIgnoreCase));
88+
89+
if (!string.IsNullOrEmpty(hostProfileIdArgument))
90+
{
91+
hostProfileId = hostProfileIdArgument.Substring(15).Trim('"');
92+
}
93+
94+
string hostName = null;
95+
string hostNameArgument =
96+
args.FirstOrDefault(
97+
arg =>
98+
arg.StartsWith(
99+
"/hostName:",
100+
StringComparison.InvariantCultureIgnoreCase));
83101

84-
ProtocolEndpoint server = null;
85-
if (runDebugAdapter)
102+
if (!string.IsNullOrEmpty(hostNameArgument))
86103
{
87-
logPath = logPath ?? "DebugAdapter.log";
88-
server = new DebugAdapter();
104+
hostName = hostNameArgument.Substring(10).Trim('"');
89105
}
90-
else
106+
107+
Version hostVersion = null;
108+
string hostVersionArgument =
109+
args.FirstOrDefault(
110+
arg =>
111+
arg.StartsWith(
112+
"/hostVersion:",
113+
StringComparison.InvariantCultureIgnoreCase));
114+
115+
if (!string.IsNullOrEmpty(hostVersionArgument))
91116
{
92-
logPath = logPath ?? "EditorServices.log";
93-
server = new LanguageServer();
117+
hostVersion =
118+
new Version(
119+
hostVersionArgument.Substring(13).Trim('"'));
94120
}
95121

122+
// Create the host details from parameters
123+
HostDetails hostDetails =
124+
new HostDetails(
125+
hostName,
126+
hostProfileId,
127+
hostVersion);
128+
129+
// Catch unhandled exceptions for logging purposes
130+
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
131+
132+
// Use a default log path filename if one isn't specified
133+
logPath =
134+
runDebugAdapter
135+
? logPath ?? "DebugAdapter.log"
136+
: logPath ?? "EditorServices.log";
137+
96138
// Start the logger with the specified log path and level
97139
Logger.Initialize(logPath, logLevel);
98140

@@ -103,9 +145,20 @@ static void Main(string[] args)
103145
Logger.Write(
104146
LogLevel.Normal,
105147
string.Format(
106-
"PowerShell Editor Services Host v{0} starting (pid {1})...",
148+
"PowerShell Editor Services Host v{0} starting (pid {1})...\r\n\r\n" +
149+
" Host application details:\r\n\r\n" +
150+
" Name: {2}\r\n ProfileId: {3}\r\n Version: {4}",
107151
fileVersionInfo.FileVersion,
108-
Process.GetCurrentProcess().Id));
152+
Process.GetCurrentProcess().Id,
153+
hostDetails.Name,
154+
hostDetails.ProfileId,
155+
hostDetails.Version));
156+
157+
// Create the appropriate server type
158+
ProtocolEndpoint server =
159+
runDebugAdapter
160+
? (ProtocolEndpoint) new DebugAdapter(hostDetails)
161+
: (ProtocolEndpoint) new LanguageServer(hostDetails);
109162

110163
// Start the server
111164
server.Start().Wait();

src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs

+6-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter;
77
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
88
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel;
9+
using Microsoft.PowerShell.EditorServices.Session;
910
using Microsoft.PowerShell.EditorServices.Utility;
1011
using System;
1112
using System.Collections.Generic;
@@ -25,14 +26,16 @@ public class DebugAdapter : DebugAdapterBase
2526
private string scriptPathToLaunch;
2627
private string arguments;
2728

28-
public DebugAdapter() : this(new StdioServerChannel())
29+
public DebugAdapter(HostDetails hostDetails)
30+
: this(hostDetails, new StdioServerChannel())
2931
{
3032
}
3133

32-
public DebugAdapter(ChannelBase serverChannel) : base(serverChannel)
34+
public DebugAdapter(HostDetails hostDetails, ChannelBase serverChannel)
35+
: base(serverChannel)
3336
{
3437
this.editorSession = new EditorSession();
35-
this.editorSession.StartSession();
38+
this.editorSession.StartSession(hostDetails);
3639
this.editorSession.DebugService.DebuggerStopped += this.DebugService_DebuggerStopped;
3740
this.editorSession.ConsoleService.OutputWritten += this.powerShellContext_OutputWritten;
3841

src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs

+24-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
using Microsoft.PowerShell.EditorServices.Protocol.LanguageServer;
77
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
88
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel;
9-
using Microsoft.PowerShell.EditorServices.Protocol.Messages;
9+
using Microsoft.PowerShell.EditorServices.Session;
1010
using Microsoft.PowerShell.EditorServices.Utility;
1111
using System;
1212
using System.Collections.Generic;
@@ -24,18 +24,27 @@ public class LanguageServer : LanguageServerBase
2424
{
2525
private static CancellationTokenSource existingRequestCancellation;
2626

27+
private bool profilesLoaded;
2728
private EditorSession editorSession;
2829
private OutputDebouncer outputDebouncer;
2930
private LanguageServerSettings currentSettings = new LanguageServerSettings();
3031

31-
public LanguageServer() : this(new StdioServerChannel())
32+
/// <param name="hostDetails">
33+
/// Provides details about the host application.
34+
/// </param>
35+
public LanguageServer(HostDetails hostDetails)
36+
: this(hostDetails, new StdioServerChannel())
3237
{
3338
}
3439

35-
public LanguageServer(ChannelBase serverChannel) : base(serverChannel)
40+
/// <param name="hostDetails">
41+
/// Provides details about the host application.
42+
/// </param>
43+
public LanguageServer(HostDetails hostDetails, ChannelBase serverChannel)
44+
: base(serverChannel)
3645
{
3746
this.editorSession = new EditorSession();
38-
this.editorSession.StartSession();
47+
this.editorSession.StartSession(hostDetails);
3948
this.editorSession.ConsoleService.OutputWritten += this.powerShellContext_OutputWritten;
4049

4150
// Always send console prompts through the UI in the language service
@@ -59,7 +68,7 @@ protected override void Initialize()
5968
this.SetEventHandler(DidOpenTextDocumentNotification.Type, this.HandleDidOpenTextDocumentNotification);
6069
this.SetEventHandler(DidCloseTextDocumentNotification.Type, this.HandleDidCloseTextDocumentNotification);
6170
this.SetEventHandler(DidChangeTextDocumentNotification.Type, this.HandleDidChangeTextDocumentNotification);
62-
this.SetEventHandler(DidChangeConfigurationNotification<SettingsWrapper>.Type, this.HandleDidChangeConfigurationNotification);
71+
this.SetEventHandler(DidChangeConfigurationNotification<LanguageServerSettingsWrapper>.Type, this.HandleDidChangeConfigurationNotification);
6372

6473
this.SetRequestHandler(DefinitionRequest.Type, this.HandleDefinitionRequest);
6574
this.SetRequestHandler(ReferencesRequest.Type, this.HandleReferencesRequest);
@@ -287,15 +296,24 @@ protected Task HandleDidChangeTextDocumentNotification(
287296
}
288297

289298
protected async Task HandleDidChangeConfigurationNotification(
290-
DidChangeConfigurationParams<SettingsWrapper> configChangeParams,
299+
DidChangeConfigurationParams<LanguageServerSettingsWrapper> configChangeParams,
291300
EventContext eventContext)
292301
{
302+
bool oldLoadProfiles = this.currentSettings.EnableProfileLoading;
293303
bool oldScriptAnalysisEnabled =
294304
this.currentSettings.ScriptAnalysis.Enable.HasValue;
295305

296306
this.currentSettings.Update(
297307
configChangeParams.Settings.Powershell);
298308

309+
if (!this.profilesLoaded &&
310+
this.currentSettings.EnableProfileLoading &&
311+
oldLoadProfiles != this.currentSettings.EnableProfileLoading)
312+
{
313+
await this.editorSession.PowerShellContext.LoadHostProfiles();
314+
this.profilesLoaded = true;
315+
}
316+
299317
if (oldScriptAnalysisEnabled != this.currentSettings.ScriptAnalysis.Enable)
300318
{
301319
// If the user just turned off script analysis, send a diagnostics

src/PowerShellEditorServices.Protocol/Server/LanguageServerSettings.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ namespace Microsoft.PowerShell.EditorServices.Protocol.Server
77
{
88
public class LanguageServerSettings
99
{
10+
public bool EnableProfileLoading { get; set; }
11+
1012
public ScriptAnalysisSettings ScriptAnalysis { get; set; }
1113

1214
public LanguageServerSettings()
@@ -18,6 +20,7 @@ public void Update(LanguageServerSettings settings)
1820
{
1921
if (settings != null)
2022
{
23+
this.EnableProfileLoading = settings.EnableProfileLoading;
2124
this.ScriptAnalysis.Update(settings.ScriptAnalysis);
2225
}
2326
}
@@ -41,7 +44,7 @@ public void Update(ScriptAnalysisSettings settings)
4144
}
4245
}
4346

44-
public class SettingsWrapper
47+
public class LanguageServerSettingsWrapper
4548
{
4649
// NOTE: This property is capitalized as 'Powershell' because the
4750
// mode name sent from the client is written as 'powershell' and

src/PowerShellEditorServices/PowerShellEditorServices.csproj

+2
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
<Compile Include="Console\ChoiceDetails.cs" />
9999
<Compile Include="Session\EditorSession.cs" />
100100
<Compile Include="Console\IConsoleHost.cs" />
101+
<Compile Include="Session\HostDetails.cs" />
101102
<Compile Include="Session\IVersionSpecificOperations.cs" />
102103
<Compile Include="Session\OutputType.cs" />
103104
<Compile Include="Session\OutputWrittenEventArgs.cs" />
@@ -107,6 +108,7 @@
107108
<Compile Include="Session\PowerShellExecutionResult.cs" />
108109
<Compile Include="Session\PowerShellContext.cs" />
109110
<Compile Include="Session\PowerShellContextState.cs" />
111+
<Compile Include="Session\ProfilePaths.cs" />
110112
<Compile Include="Session\ProgressDetails.cs" />
111113
<Compile Include="Session\RunspaceHandle.cs" />
112114
<Compile Include="Session\SessionPSHost.cs" />

src/PowerShellEditorServices/Session/EditorSession.cs

+14-6
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,9 @@
44
//
55

66
using Microsoft.PowerShell.EditorServices.Console;
7+
using Microsoft.PowerShell.EditorServices.Session;
78
using Microsoft.PowerShell.EditorServices.Utility;
8-
using System;
99
using System.IO;
10-
using System.Management.Automation;
11-
using System.Management.Automation.Runspaces;
12-
using System.Reflection;
13-
using System.Threading;
1410

1511
namespace Microsoft.PowerShell.EditorServices
1612
{
@@ -61,9 +57,21 @@ public class EditorSession
6157
/// for the ConsoleService.
6258
/// </summary>
6359
public void StartSession()
60+
{
61+
this.StartSession(null);
62+
}
63+
64+
/// <summary>
65+
/// Starts the session using the provided IConsoleHost implementation
66+
/// for the ConsoleService.
67+
/// </summary>
68+
/// <param name="hostDetails">
69+
/// Provides details about the host application.
70+
/// </param>
71+
public void StartSession(HostDetails hostDetails)
6472
{
6573
// Initialize all services
66-
this.PowerShellContext = new PowerShellContext();
74+
this.PowerShellContext = new PowerShellContext(hostDetails);
6775
this.LanguageService = new LanguageService(this.PowerShellContext);
6876
this.DebugService = new DebugService(this.PowerShellContext);
6977
this.ConsoleService = new ConsoleService(this.PowerShellContext);

0 commit comments

Comments
 (0)