|
// 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.
#nullable disable
using Microsoft.VisualStudio.Text;
namespace Microsoft.VisualStudio.LanguageServices.Implementation;
internal class ReiteratedVersionSnapshotTracker
{
/// <summary>
/// tracking text buffer
/// </summary>
private ITextBuffer _trackingBuffer;
/// <summary>
/// hold onto latest ReiteratedVersionNumber snapshot of a textbuffer
/// there is a bug where many of our features just assume that if they wait, they will end up get the latest snapshot in some ways.
/// but, unfortunately that is actually not true. they will, at the end, get latest reiterated version snapshot but
/// not the latest version snapshot since we might have skipped/swallowed the latest snapshot since its content didn't change.
/// this is especially unfortunate for features that want to move back and forth between source text and ITextSnapshot since holding
/// on the latest snapshot won't guarantee that. so, in VS, we hold onto right latest snapshot in VS workspace so that all feature under it
/// doesn't need to worry about it.
/// this could be moved down to workspace_editor if it actually move up to editor layer.
/// but for now, I am putting it here. we can think about moving it down to workspace_editor later.
/// </summary>
private ITextSnapshot _latestReiteratedVersionSnapshot;
public ReiteratedVersionSnapshotTracker(ITextBuffer buffer)
{
if (buffer != null)
{
StartTracking(buffer);
}
}
public void StartTracking(ITextBuffer buffer)
{
// buffer has changed. stop tracking old buffer
if (_trackingBuffer != null && buffer != _trackingBuffer)
{
_trackingBuffer.ChangedHighPriority -= OnTextBufferChanged;
_trackingBuffer = null;
_latestReiteratedVersionSnapshot = null;
}
// start tracking new buffer
if (buffer != null && _latestReiteratedVersionSnapshot == null)
{
_latestReiteratedVersionSnapshot = buffer.CurrentSnapshot;
_trackingBuffer = buffer;
buffer.ChangedHighPriority += OnTextBufferChanged;
}
}
public void StopTracking(ITextBuffer buffer)
{
if (_trackingBuffer == buffer && buffer != null && _latestReiteratedVersionSnapshot != null)
{
buffer.ChangedHighPriority -= OnTextBufferChanged;
_trackingBuffer = null;
_latestReiteratedVersionSnapshot = null;
}
}
private void OnTextBufferChanged(object sender, TextContentChangedEventArgs e)
{
if (sender is ITextBuffer)
{
var snapshot = _latestReiteratedVersionSnapshot;
if (snapshot != null && snapshot.Version != null && e.AfterVersion != null &&
snapshot.Version.ReiteratedVersionNumber < e.AfterVersion.ReiteratedVersionNumber)
{
_latestReiteratedVersionSnapshot = e.After;
}
}
}
}
|