|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource
{
internal class OpenDocumentTracker<TItem>
where TItem : TableItem
{
private readonly object _gate = new();
private readonly Dictionary<DocumentId, Dictionary<object, WeakReference<AbstractTableEntriesSnapshot<TItem>>>> _map =
new();
private readonly Workspace _workspace;
public OpenDocumentTracker(Workspace workspace)
{
_workspace = workspace;
_workspace.DocumentClosed += OnDocumentClosed;
_workspace.WorkspaceChanged += OnWorkspaceChanged;
}
public void TrackOpenDocument(DocumentId documentId, object id, AbstractTableEntriesSnapshot<TItem> snapshot)
{
lock (_gate)
{
if (!_map.TryGetValue(documentId, out var secondMap))
{
secondMap = new Dictionary<object, WeakReference<AbstractTableEntriesSnapshot<TItem>>>();
_map.Add(documentId, secondMap);
}
if (secondMap.TryGetValue(id, out var oldWeakSnapshot) && oldWeakSnapshot.TryGetTarget(out var oldSnapshot))
{
oldSnapshot.StopTracking();
}
secondMap[id] = new WeakReference<AbstractTableEntriesSnapshot<TItem>>(snapshot);
}
}
private void StopTracking(DocumentId documentId)
{
lock (_gate)
{
StopTracking_NoLock(documentId);
}
}
private void StopTracking(Solution solution, ProjectId? projectId = null)
{
lock (_gate)
{
foreach (var documentId in _map.Keys.Where(d => projectId == null ? true : d.ProjectId == projectId).ToList())
{
if (solution.GetDocument(documentId) != null)
{
// document still exist.
continue;
}
StopTracking_NoLock(documentId);
}
}
}
private void StopTracking_NoLock(DocumentId documentId)
{
if (!_map.TryGetValue(documentId, out var secondMap))
{
return;
}
_map.Remove(documentId);
foreach (var weakSnapshot in secondMap.Values)
{
if (!weakSnapshot.TryGetTarget(out var snapshot))
{
continue;
}
snapshot.StopTracking();
}
}
private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
{
switch (e.Kind)
{
case WorkspaceChangeKind.SolutionRemoved:
case WorkspaceChangeKind.SolutionCleared:
StopTracking(e.NewSolution);
break;
case WorkspaceChangeKind.ProjectRemoved:
StopTracking(e.NewSolution, e.ProjectId);
break;
case WorkspaceChangeKind.DocumentRemoved:
StopTracking(e.DocumentId!);
break;
default:
// do nothing
break;
}
}
private void OnDocumentClosed(object sender, DocumentEventArgs e)
=> StopTracking(e.Document.Id);
}
}
|