-
Notifications
You must be signed in to change notification settings - Fork 234
/
Copy pathSessionFileWriter.cs
148 lines (125 loc) · 5.92 KB
/
SessionFileWriter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.IO;
using System.Management.Automation;
using System.Text;
using SMA = System.Management.Automation;
namespace Microsoft.PowerShell.EditorServices.Hosting
{
/// <summary>
/// Writes the session file when the server is ready for a connection,
/// so that the client can connect.
/// </summary>
public interface ISessionFileWriter
{
/// <summary>
/// Write a session file describing a failed startup.
/// </summary>
/// <param name="reason">The reason for the startup failure.</param>
void WriteSessionFailure(string reason);
/// <summary>
/// Write a session file describing a successful startup.
/// </summary>
/// <param name="languageServiceTransport">The transport configuration for the LSP service.</param>
/// <param name="debugAdapterTransport">The transport configuration for the debug adapter service.</param>
void WriteSessionStarted(ITransportConfig languageServiceTransport, ITransportConfig debugAdapterTransport);
}
/// <summary>
/// The default session file writer, which uses PowerShell to write a session file.
/// </summary>
public sealed class SessionFileWriter : ISessionFileWriter
{
// Use BOM-less UTF-8 for session file
private static readonly Encoding s_sessionFileEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
private readonly HostLogger _logger;
private readonly string _sessionFilePath;
private readonly Version _powerShellVersion;
/// <summary>
/// Construct a new session file writer for the given session file path.
/// </summary>
/// <param name="logger">The logger to log actions with.</param>
/// <param name="sessionFilePath">The path to write the session file path to.</param>
/// <param name="powerShellVersion">The process's PowerShell version object.</param>
public SessionFileWriter(HostLogger logger, string sessionFilePath, Version powerShellVersion)
{
_logger = logger;
_sessionFilePath = sessionFilePath;
_powerShellVersion = powerShellVersion;
}
/// <summary>
/// Write a startup failure to the session file.
/// </summary>
/// <param name="reason">The reason for the startup failure.</param>
public void WriteSessionFailure(string reason)
{
_logger.Log(PsesLogLevel.Diagnostic, "Writing session failure");
Dictionary<string, object> sessionObject = new()
{
{ "status", "failed" },
{ "reason", reason },
};
WriteSessionObject(sessionObject);
}
/// <summary>
/// Write a successful server startup to the session file.
/// </summary>
/// <param name="languageServiceTransport">The LSP service transport configuration.</param>
/// <param name="debugAdapterTransport">The debug adapter transport configuration.</param>
public void WriteSessionStarted(ITransportConfig languageServiceTransport, ITransportConfig debugAdapterTransport)
{
_logger.Log(PsesLogLevel.Diagnostic, "Writing session started");
Dictionary<string, object> sessionObject = new()
{
{ "status", "started" },
};
if (languageServiceTransport is not null)
{
sessionObject["languageServiceTransport"] = languageServiceTransport.SessionFileTransportName;
if (languageServiceTransport.SessionFileEntries is not null)
{
foreach (KeyValuePair<string, object> sessionEntry in languageServiceTransport.SessionFileEntries)
{
sessionObject[$"languageService{sessionEntry.Key}"] = sessionEntry.Value;
}
}
}
if (debugAdapterTransport is not null)
{
sessionObject["debugServiceTransport"] = debugAdapterTransport.SessionFileTransportName;
if (debugAdapterTransport.SessionFileEntries != null)
{
foreach (KeyValuePair<string, object> sessionEntry in debugAdapterTransport.SessionFileEntries)
{
sessionObject[$"debugService{sessionEntry.Key}"] = sessionEntry.Value;
}
}
}
WriteSessionObject(sessionObject);
}
/// <summary>
/// Write the object representing the session file to the file by serializing it as JSON.
/// </summary>
/// <param name="sessionObject">The dictionary representing the session file.</param>
private void WriteSessionObject(Dictionary<string, object> sessionObject)
{
sessionObject["powerShellVersion"] = _powerShellVersion.ToString();
string psModulePath = Environment.GetEnvironmentVariable("PSModulePath");
string content = null;
using (SMA.PowerShell pwsh = SMA.PowerShell.Create(RunspaceMode.NewRunspace))
{
content = pwsh.AddCommand("ConvertTo-Json")
.AddParameter("InputObject", sessionObject)
.AddParameter("Depth", 10)
.AddParameter("Compress")
.Invoke<string>()[0];
// Runspace creation has a bug where it resets the PSModulePath,
// which we must correct for
Environment.SetEnvironmentVariable("PSModulePath", psModulePath);
File.WriteAllText(_sessionFilePath, content, s_sessionFileEncoding);
}
_logger.Log(PsesLogLevel.Verbose, $"Session file written to {_sessionFilePath} with content:\n{content}");
}
}
}