Skip to content

Commit a4be519

Browse files
authored
Address cross-project contamination when restoring in VS (#801)
* Address cross-project contamination when restoring in VS During the restore-on-save operation, we were using a cached instance of the "before" manifest, which was used to clean up file deltas. A side effect of this was that we used the working folder for that cached manifest. If the user makes changes to a second libman.json, then it cleans the delta between that file and the previous libman.json in its folder. That typically would appear as deleting the files out of one project when making changes the libman.json file in a different project. The fix is straightforward: cache the instance in a per-document form instead of a single stored value. * Bring changelong up to date
1 parent ec35d6d commit a4be519

2 files changed

Lines changed: 24 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Upcoming Release
44
Commit: TBD
5+
- Adds Intellisense completion for files in fileMappings
6+
- Adds fileMappings support for filesystem provider
7+
- Fixes LIB016 error when using fileMappings
8+
- Fixes issue where restore-on-save in one project in VS removes files restored in a separate project
59

610
## 3.0.71
711
Commit: 33c04f70a4f55f1cddbaddad60fc78a282b298d3

src/LibraryManager.Vsix/Json/TextviewCreationListener.cs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ namespace Microsoft.Web.LibraryManager.Vsix.Json
2929
[TextViewRole(PredefinedTextViewRoles.PrimaryDocument)]
3030
internal class TextviewCreationListener : IVsTextViewCreationListener
3131
{
32-
private Manifest _manifest;
33-
private Project _project;
3432
private ErrorListPropagator _errorList;
35-
private string _manifestPath;
33+
34+
private readonly object _manifestPropertyKey = "LibManManifest";
35+
private readonly object _manifestProjectPropertyKey = "LibManProject";
3636

3737
[Import]
3838
public ITextDocumentFactoryService DocumentService { get; set; }
@@ -70,20 +70,24 @@ public void VsTextViewCreated(IVsTextView textViewAdapter)
7070
IDependencies dependencies = DependenciesFactory.FromConfigFile(doc.FilePath);
7171
#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits
7272
// Justification: Manifest is free-threaded, don't need to use JTF here
73-
_manifest = Manifest.FromFileAsync(doc.FilePath, dependencies, CancellationToken.None).Result;
73+
Manifest manifest = Manifest.FromFileAsync(doc.FilePath, dependencies, CancellationToken.None).Result;
7474
#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits
75-
_manifestPath = doc.FilePath;
76-
_project = VsHelpers.GetDTEProjectFromConfig(_manifestPath);
75+
Project project = VsHelpers.GetDTEProjectFromConfig(doc.FilePath);
76+
77+
// Save these for later reference. Must be document-specific to avoid cross-contamination.
78+
// See https://github.com/aspnet/LibraryManager/issues/800
79+
doc.TextBuffer.Properties[_manifestPropertyKey] = manifest;
80+
doc.TextBuffer.Properties[_manifestProjectPropertyKey] = project;
7781

7882
doc.FileActionOccurred += OnFileSaved;
7983
textView.Closed += OnViewClosed;
8084

8185
_ = Task.Run(async () =>
8286
{
83-
IEnumerable<OperationResult<LibraryInstallationGoalState>> results = await LibrariesValidator.GetManifestErrorsAsync(_manifest, dependencies, CancellationToken.None).ConfigureAwait(false);
87+
IEnumerable<OperationResult<LibraryInstallationGoalState>> results = await LibrariesValidator.GetManifestErrorsAsync(manifest, dependencies, CancellationToken.None).ConfigureAwait(false);
8488
if (!results.All(r => r.Success))
8589
{
86-
AddErrorsToList(results);
90+
AddErrorsToList(results, project.Name, doc.FilePath);
8791
Telemetry.LogErrors("Fail-ManifestFileOpenWithErrors", results);
8892
}
8993
});
@@ -112,17 +116,19 @@ async Task DoRestoreOnSaveAsync()
112116

113117
if (!results.All(r => r.Success))
114118
{
115-
AddErrorsToList(results);
119+
string projectName = (textDocument.TextBuffer.Properties[_manifestProjectPropertyKey] as Project)?.Name ?? string.Empty;
120+
AddErrorsToList(results, projectName, textDocument.FilePath);
116121
Logger.LogErrorsSummary(results, OperationType.Restore);
117122
Telemetry.LogErrors("Fail-ManifestFileSaveWithErrors", results);
118123
}
119124
else
120125
{
121-
if (_manifest == null || await _manifest.RemoveUnwantedFilesAsync(newManifest, CancellationToken.None).ConfigureAwait(false))
126+
Manifest oldManifest = textDocument.TextBuffer.Properties[_manifestPropertyKey] as Manifest;
127+
if (oldManifest == null || await oldManifest.RemoveUnwantedFilesAsync(newManifest, CancellationToken.None).ConfigureAwait(false))
122128
{
123-
_manifest = newManifest;
129+
textDocument.TextBuffer.Properties[_manifestPropertyKey] = newManifest;
124130

125-
await LibraryCommandService.RestoreAsync(textDocument.FilePath, _manifest, CancellationToken.None).ConfigureAwait(false);
131+
await LibraryCommandService.RestoreAsync(textDocument.FilePath, newManifest, CancellationToken.None).ConfigureAwait(false);
126132
Telemetry.TrackUserTask("Invoke-RestoreOnSave");
127133
}
128134
else
@@ -170,9 +176,9 @@ private void OnViewClosed(object sender, EventArgs e)
170176
_errorList?.ClearErrors();
171177
}
172178

173-
private void AddErrorsToList(IEnumerable<OperationResult<LibraryInstallationGoalState>> errors)
179+
private void AddErrorsToList(IEnumerable<OperationResult<LibraryInstallationGoalState>> errors, string projectName, string manifestPath)
174180
{
175-
_errorList = new ErrorListPropagator(_project?.Name, _manifestPath);
181+
_errorList = new ErrorListPropagator(projectName, manifestPath);
176182
_errorList.HandleErrors(errors);
177183
}
178184
}

0 commit comments

Comments
 (0)