//*************************************************************************** // Copyright © 2010 Microsoft Corporation. All Rights Reserved. // This code released under the terms of the // Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.) //*************************************************************************** using System; using System.Collections.Generic; using System.Windows.Threading; using Microsoft.VisualStudio.Text; namespace DafnyLanguage { /// /// Handy reusable utility to listen for change events on the associated buffer, but /// only pass these along to a set of listeners when the user stops typing for a half second /// static class BufferIdleEventUtil { static object bufferListenersKey = new object(); static object bufferTimerKey = new object(); #region Public interface public static bool AddBufferIdleEventListener(ITextBuffer buffer, EventHandler handler) { HashSet listenersForBuffer; if (!TryGetBufferListeners(buffer, out listenersForBuffer)) listenersForBuffer = ConnectToBuffer(buffer); if (listenersForBuffer.Contains(handler)) return false; listenersForBuffer.Add(handler); return true; } public static bool RemoveBufferIdleEventListener(ITextBuffer buffer, EventHandler handler) { HashSet listenersForBuffer; if (!TryGetBufferListeners(buffer, out listenersForBuffer)) return false; if (!listenersForBuffer.Contains(handler)) return false; listenersForBuffer.Remove(handler); if (listenersForBuffer.Count == 0) DisconnectFromBuffer(buffer); return true; } #endregion #region Helpers static bool TryGetBufferListeners(ITextBuffer buffer, out HashSet listeners) { return buffer.Properties.TryGetProperty(bufferListenersKey, out listeners); } static void ClearBufferListeners(ITextBuffer buffer) { buffer.Properties.RemoveProperty(bufferListenersKey); } static bool TryGetBufferTimer(ITextBuffer buffer, out DispatcherTimer timer) { return buffer.Properties.TryGetProperty(bufferTimerKey, out timer); } static void ClearBufferTimer(ITextBuffer buffer) { DispatcherTimer timer; if (TryGetBufferTimer(buffer, out timer)) { if (timer != null) timer.Stop(); buffer.Properties.RemoveProperty(bufferTimerKey); } } static void DisconnectFromBuffer(ITextBuffer buffer) { buffer.Changed -= BufferChanged; ClearBufferListeners(buffer); ClearBufferTimer(buffer); buffer.Properties.RemoveProperty(bufferListenersKey); } static HashSet ConnectToBuffer(ITextBuffer buffer) { buffer.Changed += BufferChanged; RestartTimerForBuffer(buffer); HashSet listenersForBuffer = new HashSet(); buffer.Properties[bufferListenersKey] = listenersForBuffer; return listenersForBuffer; } static void RestartTimerForBuffer(ITextBuffer buffer) { DispatcherTimer timer; if (TryGetBufferTimer(buffer, out timer)) { timer.Stop(); } else { timer = new DispatcherTimer(DispatcherPriority.ApplicationIdle) { Interval = TimeSpan.FromMilliseconds(50) }; timer.Tick += (s, e) => { ClearBufferTimer(buffer); HashSet handlers; if (TryGetBufferListeners(buffer, out handlers)) { foreach (var handler in handlers) { handler(buffer, new EventArgs()); } } }; buffer.Properties[bufferTimerKey] = timer; } timer.Start(); } static void BufferChanged(object sender, TextContentChangedEventArgs e) { ITextBuffer buffer = sender as ITextBuffer; if (buffer == null) return; RestartTimerForBuffer(buffer); } #endregion } }