Skip to content

Commit 38dd801

Browse files
committed
Fixup Open/Unsaved file identification
1 parent dd0f79f commit 38dd801

4 files changed

Lines changed: 83 additions & 9 deletions

File tree

src/PowerShellEditorServices/Services/Extension/EditorOperationsService.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,12 @@ public async Task SaveFileAsync(string currentPath, string newSavePath)
199199
public string[] GetWorkspacePaths() => _workspaceService.WorkspacePaths.ToArray();
200200

201201
public WorkspaceOpenDocument[] GetWorkspaceOpenDocuments()
202-
=> _workspaceService.GetOpenedFiles()
203-
.Select(static scriptFile => new WorkspaceOpenDocument(scriptFile.FilePath, !scriptFile.IsInMemory))
204-
.ToArray();
202+
=> [..
203+
_workspaceService
204+
.GetOpenedFiles()
205+
.Where(static scriptFile => scriptFile.IsOpen)
206+
.Select(static scriptFile => new WorkspaceOpenDocument(scriptFile.FilePath, !scriptFile.IsInMemory))
207+
];
205208

206209
public string GetWorkspaceRelativePath(ScriptFile scriptFile) => _workspaceService.GetRelativePath(scriptFile);
207210

src/PowerShellEditorServices/Services/TextDocument/Handlers/TextDocumentHandler.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,11 @@ public override Task<Unit> Handle(DidCloseTextDocumentParams notification, Cance
111111
{
112112
fileToClose.IsOpen = false;
113113

114-
// If the file watcher is supported, only close in-memory files when this
114+
// If the file watcher is supported, only close non-file-backed documents when this
115115
// notification is triggered. This lets us keep workspace files open so we can scan
116116
// for references. When a file is deleted, the file watcher will close the file.
117-
if (!_isFileWatcherSupported || fileToClose.IsInMemory)
117+
bool isBackedByFile = notification.TextDocument.Uri.ToUri().IsFile;
118+
if (!_isFileWatcherSupported || !isBackedByFile)
118119
{
119120
_workspaceService.CloseFile(fileToClose);
120121
}
@@ -132,6 +133,9 @@ public override async Task<Unit> Handle(DidSaveTextDocumentParams notification,
132133

133134
if (savedFile != null)
134135
{
136+
// On a save, untitled files will remain in memory, so this won't change for those
137+
savedFile.IsInMemory = savedFile.IsUntitled;
138+
135139
if (_remoteFileManagerService.IsUnderRemoteTempPath(savedFile.FilePath))
136140
{
137141
await _remoteFileManagerService.SaveRemoteFileAsync(savedFile.FilePath).ConfigureAwait(false);

src/PowerShellEditorServices/Services/TextDocument/ScriptFile.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,14 @@ internal sealed class ScriptFile
5151

5252
/// <summary>
5353
/// Gets a boolean that determines whether this file is
54-
/// in-memory or not (either unsaved or non-file content).
54+
/// in-memory or not (either unsaved or non-file content) aka "dirty"
5555
/// </summary>
56-
public bool IsInMemory { get; }
56+
public bool IsInMemory { get; internal set; }
57+
58+
/// <summary>
59+
/// Getter that returns if the document is not backed by a saved file path (not in-memory).
60+
/// </summary>
61+
public bool IsUntitled => !DocumentUri?.ToUri().IsFile ?? false;
5762

5863
/// <summary>
5964
/// Gets a string containing the full contents of the file.
@@ -127,11 +132,15 @@ internal ScriptFile(
127132
// so that other operations know it's untitled/in-memory
128133
// and don't think that it's a relative path
129134
// on the file system.
130-
IsInMemory = !docUri.ToUri().IsFile;
135+
DocumentUri = docUri;
136+
137+
// Initial state of document. Untitled files are in memory by definition, otherwise files start non-dirty on a filesystem
138+
IsInMemory = IsUntitled;
139+
131140
FilePath = IsInMemory
132141
? docUri.ToString()
133142
: docUri.GetFileSystemPath();
134-
DocumentUri = docUri;
143+
135144
IsAnalysisEnabled = true;
136145
this.powerShellVersion = powerShellVersion;
137146

@@ -365,6 +374,9 @@ public void ApplyChange(FileChange fileChange)
365374
// Parse the script again to be up-to-date
366375
ParseFileContents();
367376
References.TagAsChanged();
377+
378+
// Flag the script as modified
379+
IsInMemory = true;
368380
}
369381

370382
/// <summary>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.IO;
6+
using Microsoft.Extensions.Logging.Abstractions;
7+
using Microsoft.PowerShell.EditorServices.Extensions;
8+
using Microsoft.PowerShell.EditorServices.Services;
9+
using Microsoft.PowerShell.EditorServices.Services.Extension;
10+
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
11+
using OmniSharp.Extensions.LanguageServer.Protocol;
12+
using Xunit;
13+
14+
namespace PowerShellEditorServices.Test.Extensions
15+
{
16+
[Trait("Category", "Extensions")]
17+
public class EditorOperationsServiceTests
18+
{
19+
[Fact]
20+
public void GetWorkspaceOpenDocumentsReturnsOnlyOpenDocumentsAndCurrentInMemoryState()
21+
{
22+
WorkspaceService workspaceService = new(NullLoggerFactory.Instance);
23+
24+
ScriptFile openSaved = CreateFileBuffer(workspaceService, "open-saved.ps1");
25+
openSaved.IsOpen = true;
26+
openSaved.IsInMemory = false;
27+
28+
ScriptFile openUnsaved = CreateFileBuffer(workspaceService, "open-unsaved.ps1");
29+
openUnsaved.IsOpen = true;
30+
openUnsaved.IsInMemory = true;
31+
32+
ScriptFile closed = CreateFileBuffer(workspaceService, "closed.ps1");
33+
closed.IsOpen = false;
34+
closed.IsInMemory = false;
35+
36+
EditorOperationsService editorOperationsService = new(
37+
psesHost: null,
38+
workspaceService,
39+
languageServer: null);
40+
41+
WorkspaceOpenDocument[] documents = editorOperationsService.GetWorkspaceOpenDocuments();
42+
43+
Assert.Equal(2, documents.Length);
44+
Assert.Contains(documents, static document => document.Path.EndsWith("open-saved.ps1") && document.Saved);
45+
Assert.Contains(documents, static document => document.Path.EndsWith("open-unsaved.ps1") && !document.Saved);
46+
Assert.DoesNotContain(documents, static document => document.Path.EndsWith("closed.ps1"));
47+
}
48+
49+
private static ScriptFile CreateFileBuffer(WorkspaceService workspaceService, string fileName)
50+
{
51+
string filePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"), fileName);
52+
return workspaceService.GetFileBuffer(DocumentUri.FromFileSystemPath(filePath), initialBuffer: string.Empty);
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)