module Development.IDE.Core.Debouncer
( Debouncer
, registerEvent
, newAsyncDebouncer
, noopDebouncer
) where
import Control.Concurrent.Async
import Control.Concurrent.Strict
import Control.Exception
import Control.Monad (join)
import Data.Foldable (traverse_)
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as Map
import Data.Hashable
import System.Time.Extra
newtype Debouncer k = Debouncer { Debouncer k -> Seconds -> k -> IO () -> IO ()
registerEvent :: Seconds -> k -> IO () -> IO () }
newAsyncDebouncer :: (Eq k, Hashable k) => IO (Debouncer k)
newAsyncDebouncer :: IO (Debouncer k)
newAsyncDebouncer = (Seconds -> k -> IO () -> IO ()) -> Debouncer k
forall k. (Seconds -> k -> IO () -> IO ()) -> Debouncer k
Debouncer ((Seconds -> k -> IO () -> IO ()) -> Debouncer k)
-> (Var (HashMap k (Async ())) -> Seconds -> k -> IO () -> IO ())
-> Var (HashMap k (Async ()))
-> Debouncer k
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Var (HashMap k (Async ())) -> Seconds -> k -> IO () -> IO ()
forall k.
(Eq k, Hashable k) =>
Var (HashMap k (Async ())) -> Seconds -> k -> IO () -> IO ()
asyncRegisterEvent (Var (HashMap k (Async ())) -> Debouncer k)
-> IO (Var (HashMap k (Async ()))) -> IO (Debouncer k)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HashMap k (Async ()) -> IO (Var (HashMap k (Async ())))
forall a. a -> IO (Var a)
newVar HashMap k (Async ())
forall k v. HashMap k v
Map.empty
asyncRegisterEvent :: (Eq k, Hashable k) => Var (HashMap k (Async ())) -> Seconds -> k -> IO () -> IO ()
asyncRegisterEvent :: Var (HashMap k (Async ())) -> Seconds -> k -> IO () -> IO ()
asyncRegisterEvent Var (HashMap k (Async ()))
d Seconds
0 k
k IO ()
fire = do
IO (IO ()) -> IO ()
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join (IO (IO ()) -> IO ()) -> IO (IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ Var (HashMap k (Async ()))
-> (HashMap k (Async ()) -> IO (HashMap k (Async ()), IO ()))
-> IO (IO ())
forall a b. Var a -> (a -> IO (a, b)) -> IO b
modifyVar Var (HashMap k (Async ()))
d ((HashMap k (Async ()) -> IO (HashMap k (Async ()), IO ()))
-> IO (IO ()))
-> (HashMap k (Async ()) -> IO (HashMap k (Async ()), IO ()))
-> IO (IO ())
forall a b. (a -> b) -> a -> b
$ \HashMap k (Async ())
m -> do
(IO ()
cancel, !HashMap k (Async ())
m') <- (IO (), HashMap k (Async ())) -> IO (IO (), HashMap k (Async ()))
forall a. a -> IO a
evaluate ((IO (), HashMap k (Async ())) -> IO (IO (), HashMap k (Async ())))
-> (IO (), HashMap k (Async ()))
-> IO (IO (), HashMap k (Async ()))
forall a b. (a -> b) -> a -> b
$ (Maybe (Async ()) -> (IO (), Maybe (Async ())))
-> k -> HashMap k (Async ()) -> (IO (), HashMap k (Async ()))
forall (f :: * -> *) k v.
(Functor f, Eq k, Hashable k) =>
(Maybe v -> f (Maybe v)) -> k -> HashMap k v -> f (HashMap k v)
Map.alterF (\Maybe (Async ())
prev -> ((Async () -> IO ()) -> Maybe (Async ()) -> IO ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ Async () -> IO ()
forall a. Async a -> IO ()
cancel Maybe (Async ())
prev, Maybe (Async ())
forall a. Maybe a
Nothing)) k
k HashMap k (Async ())
m
(HashMap k (Async ()), IO ()) -> IO (HashMap k (Async ()), IO ())
forall (m :: * -> *) a. Monad m => a -> m a
return (HashMap k (Async ())
m', IO ()
cancel)
IO ()
fire
asyncRegisterEvent Var (HashMap k (Async ()))
d Seconds
delay k
k IO ()
fire = IO () -> IO ()
forall a. IO a -> IO a
mask_ (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
Async ()
a <- ((forall a. IO a -> IO a) -> IO ()) -> IO (Async ())
forall a. ((forall a. IO a -> IO a) -> IO a) -> IO (Async a)
asyncWithUnmask (((forall a. IO a -> IO a) -> IO ()) -> IO (Async ()))
-> ((forall a. IO a -> IO a) -> IO ()) -> IO (Async ())
forall a b. (a -> b) -> a -> b
$ \forall a. IO a -> IO a
unmask -> IO () -> IO ()
forall a. IO a -> IO a
unmask (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
Seconds -> IO ()
sleep Seconds
delay
IO ()
fire
Var (HashMap k (Async ()))
-> (HashMap k (Async ()) -> IO (HashMap k (Async ()))) -> IO ()
forall a. Var a -> (a -> IO a) -> IO ()
modifyVar_ Var (HashMap k (Async ()))
d (HashMap k (Async ()) -> IO (HashMap k (Async ()))
forall a. a -> IO a
evaluate (HashMap k (Async ()) -> IO (HashMap k (Async ())))
-> (HashMap k (Async ()) -> HashMap k (Async ()))
-> HashMap k (Async ())
-> IO (HashMap k (Async ()))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. k -> HashMap k (Async ()) -> HashMap k (Async ())
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> HashMap k v
Map.delete k
k)
IO (IO ()) -> IO ()
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join (IO (IO ()) -> IO ()) -> IO (IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ Var (HashMap k (Async ()))
-> (HashMap k (Async ()) -> IO (HashMap k (Async ()), IO ()))
-> IO (IO ())
forall a b. Var a -> (a -> IO (a, b)) -> IO b
modifyVar Var (HashMap k (Async ()))
d ((HashMap k (Async ()) -> IO (HashMap k (Async ()), IO ()))
-> IO (IO ()))
-> (HashMap k (Async ()) -> IO (HashMap k (Async ()), IO ()))
-> IO (IO ())
forall a b. (a -> b) -> a -> b
$ \HashMap k (Async ())
m -> do
(IO ()
cancel, !HashMap k (Async ())
m') <- (IO (), HashMap k (Async ())) -> IO (IO (), HashMap k (Async ()))
forall a. a -> IO a
evaluate ((IO (), HashMap k (Async ())) -> IO (IO (), HashMap k (Async ())))
-> (IO (), HashMap k (Async ()))
-> IO (IO (), HashMap k (Async ()))
forall a b. (a -> b) -> a -> b
$ (Maybe (Async ()) -> (IO (), Maybe (Async ())))
-> k -> HashMap k (Async ()) -> (IO (), HashMap k (Async ()))
forall (f :: * -> *) k v.
(Functor f, Eq k, Hashable k) =>
(Maybe v -> f (Maybe v)) -> k -> HashMap k v -> f (HashMap k v)
Map.alterF (\Maybe (Async ())
prev -> ((Async () -> IO ()) -> Maybe (Async ()) -> IO ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ Async () -> IO ()
forall a. Async a -> IO ()
cancel Maybe (Async ())
prev, Async () -> Maybe (Async ())
forall a. a -> Maybe a
Just Async ()
a)) k
k HashMap k (Async ())
m
(HashMap k (Async ()), IO ()) -> IO (HashMap k (Async ()), IO ())
forall (m :: * -> *) a. Monad m => a -> m a
return (HashMap k (Async ())
m', IO ()
cancel)
noopDebouncer :: Debouncer k
noopDebouncer :: Debouncer k
noopDebouncer = (Seconds -> k -> IO () -> IO ()) -> Debouncer k
forall k. (Seconds -> k -> IO () -> IO ()) -> Debouncer k
Debouncer ((Seconds -> k -> IO () -> IO ()) -> Debouncer k)
-> (Seconds -> k -> IO () -> IO ()) -> Debouncer k
forall a b. (a -> b) -> a -> b
$ \Seconds
_ k
_ IO ()
a -> IO ()
a