diff --git a/src/PowerShellEditorServices.Channel.WebSocket/WebsocketServerChannel.cs b/src/PowerShellEditorServices.Channel.WebSocket/WebsocketServerChannel.cs
index 90586818b..01906d6cd 100644
--- a/src/PowerShellEditorServices.Channel.WebSocket/WebsocketServerChannel.cs
+++ b/src/PowerShellEditorServices.Channel.WebSocket/WebsocketServerChannel.cs
@@ -139,7 +139,7 @@ public class LanguageServerWebSocketConnection : EditorServiceWebSocketConnectio
{
public LanguageServerWebSocketConnection()
{
- Server = new LanguageServer(Channel);
+ Server = new LanguageServer(null, Channel);
}
}
@@ -150,7 +150,7 @@ public class DebugAdapterWebSocketConnection : EditorServiceWebSocketConnection
{
public DebugAdapterWebSocketConnection()
{
- Server = new DebugAdapter(Channel);
+ Server = new DebugAdapter(null, Channel);
}
}
diff --git a/src/PowerShellEditorServices.Host/Program.cs b/src/PowerShellEditorServices.Host/Program.cs
index 95b146c6d..fc2cd06ff 100644
--- a/src/PowerShellEditorServices.Host/Program.cs
+++ b/src/PowerShellEditorServices.Host/Program.cs
@@ -5,11 +5,11 @@
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
using Microsoft.PowerShell.EditorServices.Protocol.Server;
+using Microsoft.PowerShell.EditorServices.Session;
using Microsoft.PowerShell.EditorServices.Utility;
using System;
using System.Diagnostics;
using System.Linq;
-using System.Threading;
namespace Microsoft.PowerShell.EditorServices.Host
{
@@ -78,21 +78,63 @@ static void Main(string[] args)
"/debugAdapter",
StringComparison.InvariantCultureIgnoreCase));
- // Catch unhandled exceptions for logging purposes
- AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
+ string hostProfileId = null;
+ string hostProfileIdArgument =
+ args.FirstOrDefault(
+ arg =>
+ arg.StartsWith(
+ "/hostProfileId:",
+ StringComparison.InvariantCultureIgnoreCase));
+
+ if (!string.IsNullOrEmpty(hostProfileIdArgument))
+ {
+ hostProfileId = hostProfileIdArgument.Substring(15).Trim('"');
+ }
+
+ string hostName = null;
+ string hostNameArgument =
+ args.FirstOrDefault(
+ arg =>
+ arg.StartsWith(
+ "/hostName:",
+ StringComparison.InvariantCultureIgnoreCase));
- ProtocolEndpoint server = null;
- if (runDebugAdapter)
+ if (!string.IsNullOrEmpty(hostNameArgument))
{
- logPath = logPath ?? "DebugAdapter.log";
- server = new DebugAdapter();
+ hostName = hostNameArgument.Substring(10).Trim('"');
}
- else
+
+ Version hostVersion = null;
+ string hostVersionArgument =
+ args.FirstOrDefault(
+ arg =>
+ arg.StartsWith(
+ "/hostVersion:",
+ StringComparison.InvariantCultureIgnoreCase));
+
+ if (!string.IsNullOrEmpty(hostVersionArgument))
{
- logPath = logPath ?? "EditorServices.log";
- server = new LanguageServer();
+ hostVersion =
+ new Version(
+ hostVersionArgument.Substring(13).Trim('"'));
}
+ // Create the host details from parameters
+ HostDetails hostDetails =
+ new HostDetails(
+ hostName,
+ hostProfileId,
+ hostVersion);
+
+ // Catch unhandled exceptions for logging purposes
+ AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
+
+ // Use a default log path filename if one isn't specified
+ logPath =
+ runDebugAdapter
+ ? logPath ?? "DebugAdapter.log"
+ : logPath ?? "EditorServices.log";
+
// Start the logger with the specified log path and level
Logger.Initialize(logPath, logLevel);
@@ -103,9 +145,20 @@ static void Main(string[] args)
Logger.Write(
LogLevel.Normal,
string.Format(
- "PowerShell Editor Services Host v{0} starting (pid {1})...",
+ "PowerShell Editor Services Host v{0} starting (pid {1})...\r\n\r\n" +
+ " Host application details:\r\n\r\n" +
+ " Name: {2}\r\n ProfileId: {3}\r\n Version: {4}",
fileVersionInfo.FileVersion,
- Process.GetCurrentProcess().Id));
+ Process.GetCurrentProcess().Id,
+ hostDetails.Name,
+ hostDetails.ProfileId,
+ hostDetails.Version));
+
+ // Create the appropriate server type
+ ProtocolEndpoint server =
+ runDebugAdapter
+ ? (ProtocolEndpoint) new DebugAdapter(hostDetails)
+ : (ProtocolEndpoint) new LanguageServer(hostDetails);
// Start the server
server.Start().Wait();
diff --git a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs
index a357535e9..7a3b58b79 100644
--- a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs
+++ b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs
@@ -6,6 +6,7 @@
using Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter;
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel;
+using Microsoft.PowerShell.EditorServices.Session;
using Microsoft.PowerShell.EditorServices.Utility;
using System;
using System.Collections.Generic;
@@ -25,14 +26,16 @@ public class DebugAdapter : DebugAdapterBase
private string scriptPathToLaunch;
private string arguments;
- public DebugAdapter() : this(new StdioServerChannel())
+ public DebugAdapter(HostDetails hostDetails)
+ : this(hostDetails, new StdioServerChannel())
{
}
- public DebugAdapter(ChannelBase serverChannel) : base(serverChannel)
+ public DebugAdapter(HostDetails hostDetails, ChannelBase serverChannel)
+ : base(serverChannel)
{
this.editorSession = new EditorSession();
- this.editorSession.StartSession();
+ this.editorSession.StartSession(hostDetails);
this.editorSession.DebugService.DebuggerStopped += this.DebugService_DebuggerStopped;
this.editorSession.ConsoleService.OutputWritten += this.powerShellContext_OutputWritten;
diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs
index 689b64b83..f62f59ed1 100644
--- a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs
+++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs
@@ -6,7 +6,7 @@
using Microsoft.PowerShell.EditorServices.Protocol.LanguageServer;
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel;
-using Microsoft.PowerShell.EditorServices.Protocol.Messages;
+using Microsoft.PowerShell.EditorServices.Session;
using Microsoft.PowerShell.EditorServices.Utility;
using System;
using System.Collections.Generic;
@@ -24,18 +24,27 @@ public class LanguageServer : LanguageServerBase
{
private static CancellationTokenSource existingRequestCancellation;
+ private bool profilesLoaded;
private EditorSession editorSession;
private OutputDebouncer outputDebouncer;
private LanguageServerSettings currentSettings = new LanguageServerSettings();
- public LanguageServer() : this(new StdioServerChannel())
+ ///
+ /// Provides details about the host application.
+ ///
+ public LanguageServer(HostDetails hostDetails)
+ : this(hostDetails, new StdioServerChannel())
{
}
- public LanguageServer(ChannelBase serverChannel) : base(serverChannel)
+ ///
+ /// Provides details about the host application.
+ ///
+ public LanguageServer(HostDetails hostDetails, ChannelBase serverChannel)
+ : base(serverChannel)
{
this.editorSession = new EditorSession();
- this.editorSession.StartSession();
+ this.editorSession.StartSession(hostDetails);
this.editorSession.ConsoleService.OutputWritten += this.powerShellContext_OutputWritten;
// Always send console prompts through the UI in the language service
@@ -59,7 +68,7 @@ protected override void Initialize()
this.SetEventHandler(DidOpenTextDocumentNotification.Type, this.HandleDidOpenTextDocumentNotification);
this.SetEventHandler(DidCloseTextDocumentNotification.Type, this.HandleDidCloseTextDocumentNotification);
this.SetEventHandler(DidChangeTextDocumentNotification.Type, this.HandleDidChangeTextDocumentNotification);
- this.SetEventHandler(DidChangeConfigurationNotification.Type, this.HandleDidChangeConfigurationNotification);
+ this.SetEventHandler(DidChangeConfigurationNotification.Type, this.HandleDidChangeConfigurationNotification);
this.SetRequestHandler(DefinitionRequest.Type, this.HandleDefinitionRequest);
this.SetRequestHandler(ReferencesRequest.Type, this.HandleReferencesRequest);
@@ -287,15 +296,24 @@ protected Task HandleDidChangeTextDocumentNotification(
}
protected async Task HandleDidChangeConfigurationNotification(
- DidChangeConfigurationParams configChangeParams,
+ DidChangeConfigurationParams configChangeParams,
EventContext eventContext)
{
+ bool oldLoadProfiles = this.currentSettings.EnableProfileLoading;
bool oldScriptAnalysisEnabled =
this.currentSettings.ScriptAnalysis.Enable.HasValue;
this.currentSettings.Update(
configChangeParams.Settings.Powershell);
+ if (!this.profilesLoaded &&
+ this.currentSettings.EnableProfileLoading &&
+ oldLoadProfiles != this.currentSettings.EnableProfileLoading)
+ {
+ await this.editorSession.PowerShellContext.LoadHostProfiles();
+ this.profilesLoaded = true;
+ }
+
if (oldScriptAnalysisEnabled != this.currentSettings.ScriptAnalysis.Enable)
{
// If the user just turned off script analysis, send a diagnostics
diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServerSettings.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServerSettings.cs
index 256a81c99..ae4970687 100644
--- a/src/PowerShellEditorServices.Protocol/Server/LanguageServerSettings.cs
+++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServerSettings.cs
@@ -7,6 +7,8 @@ namespace Microsoft.PowerShell.EditorServices.Protocol.Server
{
public class LanguageServerSettings
{
+ public bool EnableProfileLoading { get; set; }
+
public ScriptAnalysisSettings ScriptAnalysis { get; set; }
public LanguageServerSettings()
@@ -18,6 +20,7 @@ public void Update(LanguageServerSettings settings)
{
if (settings != null)
{
+ this.EnableProfileLoading = settings.EnableProfileLoading;
this.ScriptAnalysis.Update(settings.ScriptAnalysis);
}
}
@@ -41,7 +44,7 @@ public void Update(ScriptAnalysisSettings settings)
}
}
- public class SettingsWrapper
+ public class LanguageServerSettingsWrapper
{
// NOTE: This property is capitalized as 'Powershell' because the
// mode name sent from the client is written as 'powershell' and
diff --git a/src/PowerShellEditorServices/PowerShellEditorServices.csproj b/src/PowerShellEditorServices/PowerShellEditorServices.csproj
index 914047a31..ef6e2f1fa 100644
--- a/src/PowerShellEditorServices/PowerShellEditorServices.csproj
+++ b/src/PowerShellEditorServices/PowerShellEditorServices.csproj
@@ -98,6 +98,7 @@
+
@@ -107,6 +108,7 @@
+
diff --git a/src/PowerShellEditorServices/Session/EditorSession.cs b/src/PowerShellEditorServices/Session/EditorSession.cs
index a06c10e64..6392d96c7 100644
--- a/src/PowerShellEditorServices/Session/EditorSession.cs
+++ b/src/PowerShellEditorServices/Session/EditorSession.cs
@@ -4,13 +4,9 @@
//
using Microsoft.PowerShell.EditorServices.Console;
+using Microsoft.PowerShell.EditorServices.Session;
using Microsoft.PowerShell.EditorServices.Utility;
-using System;
using System.IO;
-using System.Management.Automation;
-using System.Management.Automation.Runspaces;
-using System.Reflection;
-using System.Threading;
namespace Microsoft.PowerShell.EditorServices
{
@@ -61,9 +57,21 @@ public class EditorSession
/// for the ConsoleService.
///
public void StartSession()
+ {
+ this.StartSession(null);
+ }
+
+ ///
+ /// Starts the session using the provided IConsoleHost implementation
+ /// for the ConsoleService.
+ ///
+ ///
+ /// Provides details about the host application.
+ ///
+ public void StartSession(HostDetails hostDetails)
{
// Initialize all services
- this.PowerShellContext = new PowerShellContext();
+ this.PowerShellContext = new PowerShellContext(hostDetails);
this.LanguageService = new LanguageService(this.PowerShellContext);
this.DebugService = new DebugService(this.PowerShellContext);
this.ConsoleService = new ConsoleService(this.PowerShellContext);
diff --git a/src/PowerShellEditorServices/Session/HostDetails.cs b/src/PowerShellEditorServices/Session/HostDetails.cs
new file mode 100644
index 000000000..a20bc8cf3
--- /dev/null
+++ b/src/PowerShellEditorServices/Session/HostDetails.cs
@@ -0,0 +1,92 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System;
+
+namespace Microsoft.PowerShell.EditorServices.Session
+{
+ ///
+ /// Contains details about the current host application (most
+ /// likely the editor which is using the host process).
+ ///
+ public class HostDetails
+ {
+ #region Constants
+
+ ///
+ /// The default host name for PowerShell Editor Services. Used
+ /// if no host name is specified by the host application.
+ ///
+ public const string DefaultHostName = "PowerShell Editor Services Host";
+
+ ///
+ /// The default host ID for PowerShell Editor Services. Used
+ /// for the host-specific profile path if no host ID is specified.
+ ///
+ public const string DefaultHostProfileId = "Microsoft.PowerShellEditorServices";
+
+ ///
+ /// The default host version for PowerShell Editor Services. If
+ /// no version is specified by the host application, we use 0.0.0
+ /// to indicate a lack of version.
+ ///
+ public static readonly Version DefaultHostVersion = new Version("0.0.0");
+
+ ///
+ /// The default host details in a HostDetails object.
+ ///
+ public static readonly HostDetails Default = new HostDetails(null, null, null);
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets the name of the host.
+ ///
+ public string Name { get; private set; }
+
+ ///
+ /// Gets the profile ID of the host, used to determine the
+ /// host-specific profile path.
+ ///
+ public string ProfileId { get; private set; }
+
+ ///
+ /// Gets the version of the host.
+ ///
+ public Version Version { get; private set; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Creates an instance of the HostDetails class.
+ ///
+ ///
+ /// The display name for the host, typically in the form of
+ /// "[Application Name] Host".
+ ///
+ ///
+ /// The identifier of the PowerShell host to use for its profile path.
+ /// loaded. Used to resolve a profile path of the form 'X_profile.ps1'
+ /// where 'X' represents the value of hostProfileId. If null, a default
+ /// will be used.
+ ///
+ /// The host application's version.
+ public HostDetails(
+ string name,
+ string profileId,
+ Version version)
+ {
+ this.Name = name ?? DefaultHostName;
+ this.ProfileId = profileId ?? DefaultHostProfileId;
+ this.Version = version ?? DefaultHostVersion;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/PowerShellEditorServices/Session/PowerShellContext.cs b/src/PowerShellEditorServices/Session/PowerShellContext.cs
index dd90c951b..6771183b1 100644
--- a/src/PowerShellEditorServices/Session/PowerShellContext.cs
+++ b/src/PowerShellEditorServices/Session/PowerShellContext.cs
@@ -37,6 +37,7 @@ public class PowerShellContext : IDisposable
private bool ownsInitialRunspace;
private Runspace initialRunspace;
private Runspace currentRunspace;
+ private ProfilePaths profilePaths;
private ConsoleServicePSHost psHost;
private InitialSessionState initialSessionState;
private IVersionSpecificOperations versionSpecificOperations;
@@ -104,9 +105,20 @@ internal IConsoleHost ConsoleHost
/// Initializes a new instance of the PowerShellContext class and
/// opens a runspace to be used for the session.
///
- public PowerShellContext()
+ public PowerShellContext() : this(null)
{
- this.psHost = new ConsoleServicePSHost();
+ }
+
+ ///
+ /// Initializes a new instance of the PowerShellContext class and
+ /// opens a runspace to be used for the session.
+ ///
+ /// Provides details about the host application.
+ public PowerShellContext(HostDetails hostDetails)
+ {
+ hostDetails = hostDetails ?? HostDetails.Default;
+
+ this.psHost = new ConsoleServicePSHost(hostDetails);
this.initialSessionState = InitialSessionState.CreateDefault2();
Runspace runspace = RunspaceFactory.CreateRunspace(psHost, this.initialSessionState);
@@ -116,7 +128,7 @@ public PowerShellContext()
this.ownsInitialRunspace = true;
- this.Initialize(runspace);
+ this.Initialize(hostDetails, runspace);
// Use reflection to execute ConsoleVisibility.AlwaysCaptureApplicationIO = true;
Type consoleVisibilityType =
@@ -141,13 +153,14 @@ public PowerShellContext()
/// Initializes a new instance of the PowerShellContext class using
/// an existing runspace for the session.
///
- ///
- public PowerShellContext(Runspace initialRunspace)
+ /// The initial runspace to use for this instance.
+ /// Provides details about the host application.
+ public PowerShellContext(HostDetails hostDetails, Runspace initialRunspace)
{
- this.Initialize(initialRunspace);
+ this.Initialize(hostDetails, initialRunspace);
}
- private void Initialize(Runspace initialRunspace)
+ private void Initialize(HostDetails hostDetails, Runspace initialRunspace)
{
Validate.IsNotNull("initialRunspace", initialRunspace);
@@ -160,7 +173,6 @@ private void Initialize(Runspace initialRunspace)
this.currentRunspace.Debugger.DebuggerStop += OnDebuggerStop;
this.powerShell = PowerShell.Create();
- this.powerShell.InvocationStateChanged += powerShell_InvocationStateChanged;
this.powerShell.Runspace = this.currentRunspace;
// TODO: Should this be configurable?
@@ -199,6 +211,14 @@ private void Initialize(Runspace initialRunspace)
this.versionSpecificOperations.ConfigureDebugger(
this.currentRunspace);
+ // Set the $profile variable in the runspace
+ this.profilePaths =
+ this.SetProfileVariableInCurrentRunspace(
+ hostDetails);
+
+ // Now that initialization is complete we can watch for InvocationStateChanged
+ this.powerShell.InvocationStateChanged += powerShell_InvocationStateChanged;
+
this.SessionState = PowerShellContextState.Ready;
// Now that the runspace is ready, enqueue it for first use
@@ -493,6 +513,24 @@ public async Task ExecuteScriptAtPath(string scriptPath, string arguments = null
await this.ExecuteCommand