Skip to content

Commit 4745a5b

Browse files
committed
Enable saving remote files opened with psedit
This change enables the saving of files opened from a remote session using the psedit command. When the editor saves the file contents it sends a DidSaveTextDocumentNotification to the language server which in turn sends the document's contents back to the remote session to be saved in the original file. Resolves PowerShell/vscode-powershell#583.
1 parent 737b8cf commit 4745a5b

File tree

3 files changed

+85
-0
lines changed

3 files changed

+85
-0
lines changed

src/PowerShellEditorServices.Protocol/LanguageServer/TextDocument.cs

+12
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ public static readonly
5252
EventType<TextDocumentIdentifier>.Create("textDocument/didClose");
5353
}
5454

55+
public class DidSaveTextDocumentNotification
56+
{
57+
public static readonly
58+
EventType<DidSaveTextDocumentParams> Type =
59+
EventType<DidSaveTextDocumentParams>.Create("textDocument/didSave");
60+
}
61+
62+
public class DidSaveTextDocumentParams
63+
{
64+
public TextDocumentIdentifier TextDocument { get; set; }
65+
}
66+
5567
public class DidChangeTextDocumentNotification
5668
{
5769
public static readonly

src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs

+18
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ protected override void Initialize()
110110

111111
this.SetEventHandler(DidOpenTextDocumentNotification.Type, this.HandleDidOpenTextDocumentNotification);
112112
this.SetEventHandler(DidCloseTextDocumentNotification.Type, this.HandleDidCloseTextDocumentNotification);
113+
this.SetEventHandler(DidSaveTextDocumentNotification.Type, this.HandleDidSaveTextDocumentNotification);
113114
this.SetEventHandler(DidChangeTextDocumentNotification.Type, this.HandleDidChangeTextDocumentNotification);
114115
this.SetEventHandler(DidChangeConfigurationNotification<LanguageServerSettingsWrapper>.Type, this.HandleDidChangeConfigurationNotification);
115116

@@ -516,6 +517,23 @@ protected async Task HandleDidCloseTextDocumentNotification(
516517

517518
Logger.Write(LogLevel.Verbose, "Finished closing document.");
518519
}
520+
protected async Task HandleDidSaveTextDocumentNotification(
521+
DidSaveTextDocumentParams saveParams,
522+
EventContext eventContext)
523+
{
524+
ScriptFile savedFile =
525+
this.editorSession.Workspace.GetFile(
526+
saveParams.TextDocument.Uri);
527+
528+
if (savedFile != null)
529+
{
530+
if (this.editorSession.RemoteFileManager.IsUnderRemoteTempPath(savedFile.FilePath))
531+
{
532+
await this.editorSession.RemoteFileManager.SaveRemoteFile(
533+
savedFile.FilePath);
534+
}
535+
}
536+
}
519537

520538
protected Task HandleDidChangeTextDocumentNotification(
521539
DidChangeTextDocumentParams textChangeParams,

src/PowerShellEditorServices/Session/RemoteFileManager.cs

+55
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using System.Linq;
1313
using System.Management.Automation;
1414
using System.Management.Automation.Runspaces;
15+
using System.Text;
1516
using System.Threading.Tasks;
1617

1718
namespace Microsoft.PowerShell.EditorServices.Session
@@ -77,6 +78,15 @@ public class RemoteFileManager
7778
Get-EventSubscriber -SourceIdentifier PSESRemoteSessionOpenFile -EA Ignore | Remove-Event
7879
";
7980

81+
private const string SetRemoteContentsScript = @"
82+
param(
83+
[string] $RemoteFilePath,
84+
[byte[]] $Content
85+
)
86+
87+
Set-Content -Path $RemoteFilePath -Value $Content -Encoding Byte -Force 2>&1
88+
";
89+
8090
#endregion
8191

8292
#region Constructors
@@ -185,6 +195,51 @@ public async Task<string> FetchRemoteFile(
185195
return localFilePath;
186196
}
187197

198+
public async Task SaveRemoteFile(string localFilePath)
199+
{
200+
string remoteFilePath =
201+
this.GetMappedPath(
202+
localFilePath,
203+
this.powerShellContext.CurrentRunspace);
204+
205+
Logger.Write(
206+
LogLevel.Verbose,
207+
$"Saving remote file {remoteFilePath} (local path: {localFilePath})");
208+
209+
byte[] localFileContents = null;
210+
try
211+
{
212+
localFileContents = File.ReadAllBytes(localFilePath);
213+
}
214+
catch (IOException e)
215+
{
216+
Logger.WriteException(
217+
"Failed to read contents of local copy of remote file",
218+
e);
219+
220+
return;
221+
}
222+
223+
PSCommand saveCommand = new PSCommand();
224+
saveCommand
225+
.AddScript(SetRemoteContentsScript)
226+
.AddParameter("RemoteFilePath", remoteFilePath)
227+
.AddParameter("Content", localFileContents);
228+
229+
StringBuilder errorMessages = new StringBuilder();
230+
231+
await this.powerShellContext.ExecuteCommand<object>(
232+
saveCommand,
233+
errorMessages,
234+
false,
235+
false);
236+
237+
if (errorMessages.Length > 0)
238+
{
239+
Logger.Write(LogLevel.Error, $"Remote file save failed due to an error:\r\n\r\n{errorMessages}");
240+
}
241+
}
242+
188243
/// <summary>
189244
/// Creates a temporary file with the given name and contents
190245
/// corresponding to the specified runspace.

0 commit comments

Comments
 (0)